1 Why the project?

Context:

What I am hoping to achieve with the project?

Key points:

2 Data collection

——————————————- ** add a screenshot of the app **

Disclaimer: I am in no shape or form affiliated with Toggl. I started using it a few years ago because I loved its minimalistic design and yet it provided all the functionality I needed. I am using it on a free membership basis.

knitr::opts_chunk$set(
    echo = TRUE, # show all of the code
    tidy = FALSE, # cleaner code printing
    size = "small", # smaller code
    
    fig.path = "figs/",# where the figures will end up
    out.width = "100%",

    message = FALSE,
    warning = FALSE
    )

3 Exploratory Data Analysis (EDA)

3.1 Piano practice timeline

timeline <- raw_data%>%
  group_by(Month_format)%>%
  summarise(Total_Duration = sum(Duration)/60)%>%
  mutate(Total_Duration2 = cumsum(Total_Duration),
         max = as.integer(max(Total_Duration2)),
         max = ifelse(max > Total_Duration2, "", max))

  #correct exam dates
  #can be automated ifelse?
  
  ggplot(timeline, aes(Month_format, Total_Duration2, group = 1))+
  geom_line(size = 2, color = "#69b3a2")+
  geom_point(size = 5, color = "#69b3a2")+
  geom_area(alpha = 0.3, fill = "#69b3a2")+
  #grade 3
  geom_point(x="Oct\n '19", y = 300+393.28333, size = 5, color = "dark red")+
  geom_text(x="Oct\n '19", y = 300+443.28333, label = "Grade 3")+
  #grade 5
  geom_point(x="Oct\n '20", y = 300+795.86667, size = 5, color = "dark red")+
  geom_text(x="Oct\n '20", y = 1140.86667,  size = 5, label = "Grade 5")+
  geom_text(x="Oct\n '20", y = 300+745.86667,  size = 5, label = "840 hours")+
  # NOW
  geom_point(aes(x="Apr\n '21", y = 1219), size = 5, color = "dark red")+
  geom_text(aes(label = max), nudge_y = 75, nudge_x = -0.5, size = 5)+
  scale_fill_gradient(low="yellow", high="red")+
  labs(x = NULL,
       title = "Piano practice timeline")+
  theme_ipsum_es()+
  theme(legend.position = "top")

3.2 Animation

raw_data%>%
  filter(Date_Start > as.Date("2018/11/01"))%>%
  group_by(Project, Date_Start)%>%
  summarise(Duration = sum(Duration)/60)%>%
  mutate(Cumulative_Piece = cumsum(Duration),
         Month_Year = as.factor(as.yearmon(Date_Start)),
         Month_format = str_replace(Month_Year, " 20", "\n '"))%>%
  ungroup()%>%
  mutate(Cumulative_Total = cumsum(Duration))%>%
  filter(Project %notin% c("Technique", "General", "Sightreading"))%>%
  left_join(model_data%>%select(Level, Project, ABRSM), by = "Project")%>%
  
#fix letter issue UTC

ggplot(aes(Date_Start, Cumulative_Piece, fill = Level)) +
  geom_point(size = 10, shape = 21, col = "black", alpha = 0.5) +
  scale_size(range = c(.1, 16), guide = FALSE) +
  #geom_text(aes(x = as.Date("2020-05-01"), y = 40, label = Month_Year), size = 15, color = 'lightgrey', family = 'Oswald') +
  labs(title = 'Year: {frame_time}',
       y = "Total practice time per piece (hours)")+
  scale_color_tron()+
  scale_fill_tron()+
  theme_ipsum_es() +
  theme(legend.position = "top")+
  transition_time(Date_Start) +
  ease_aes('linear')+
  exit_fade() +
  shadow_mark(alpha = 0.1, size = 5)

#save animation as gif for later use
anim_save("figs/timeline.gif")

#make geoms persist https://stackoverflow.com/questions/63913059/is-there-a-way-to-make-geoms-fade-but-persist-in-gganimate

3.3 How consistent was my practice?

Generally, I’ve done pretty well to maintain a high level of consistency with the exception of August/December, whenever I go on Annual Leave.

raw_data%>%
  filter(Source != "Estimated")%>%
  group_by(Month_Year, Month_Start, Month_format)%>%
  summarise(Days_Practice = n_distinct(Date_Start),
            Total_Duration = sum(Duration, na.rm = TRUE))%>%
  mutate(Days_Total = days_in_month(Month_Start),
         Days_Not_Practiced = Days_Total - Days_Practice,
         Avg_Duration = as.integer(Total_Duration/Days_Total),
         Consistency = round(Days_Practice / Days_Total * 100,2),
         Consistency_Status = ifelse(Consistency<75, "Bad", "Good"),
         Month_format = reorder(Month_format, Month_Year))%>%
  
  ggplot(aes(Month_format, Consistency, fill = Consistency_Status))+
  geom_col(group = 1, col = "black")+
  geom_hline(yintercept = 75, lty = "dashed")+
  geom_text(aes(label = Days_Not_Practiced), size = 5, nudge_y = 3)+
  labs(x = NULL,
       fill = "Consistency status",
       subtitle = "Numbers indicate days without any practice within each month")+
  scale_color_tron()+
  scale_fill_tron()+
  theme_ipsum_es()+
  theme(legend.position = "top")

3.4 Was there a trend in my amount of daily average practice?

We can see that these were correlated with the consistency, where the average session was much shorter in the months I was away from the piano. There’s also a trend where my practice close to an exam session was significantly higher than any other time of the year. Can you spot in which month I had my exam in 2019? What about 2020?

average practice length per month includes the days in which I did not practice

overall

raw_data%>%
  filter(Source != "Estimated")%>%
  group_by(Month_Year, Month_Start, Month_format)%>%
  summarise(Days_Practice = n_distinct(Date_Start),
            Total_Duration = sum(Duration))%>%
  mutate(Days_Total = days_in_month(Month_Start),
         Avg_Duration = as.integer(Total_Duration/Days_Total),
         Avg_Duration_Status = ifelse(Avg_Duration < 60, "Less than one hour", "One hour"),
         Month_format = reorder(Month_format, Month_Year))%>%
  
  ggplot(aes(Month_format, Avg_Duration, fill = Avg_Duration_Status))+
  geom_col(col = "black")+
  labs(x = NULL,
       y = "Average practice session length (min)",
       fill = "Status")+
  geom_text(aes(label = Avg_Duration), nudge_y = 5, size = 5)+
  scale_color_tron()+
  scale_fill_tron()+
  theme_ipsum_es()+
  theme(legend.position = "top",
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank())

Year on Year

Similar trends as before are apparent where my average daily session is longer before the exams than any other time in the year and a dip in the months where I usually take most of my annual leave. I really do need to start picking up the pace and get back to where I used to be.

raw_data%>%
  group_by(Month_Year, Month_Start, Month_format, Month_Name, Year)%>%
  summarise(Days_Practice = n_distinct(Date_Start),
            Total_Duration = sum(Duration))%>%
  mutate(Days_Total = days_in_month(Month_Start),
         Avg_Duration = as.integer(Total_Duration/Days_Total),
         Avg_Duration_Status = ifelse(Avg_Duration < 60, "Less than one hour", "One hour"),
         Month_format = reorder(Month_format, Month_Year),
         size = as.factor(ifelse(Year == 2018, 1, 1.5)),
         label = ifelse(month(Month_Start) == 1, as.character(Year), ""))%>%
  
  ggplot(aes(Month_Name, Avg_Duration, group = Year, size = size))+
  geom_line(aes(col = Year))+
  geom_label_repel(aes(label = label, col = Year))+
  labs(x = NULL,
       fill = "Status")+
  scale_color_tron()+
  scale_fill_tron()+
  theme_ipsum_es()+
  theme(legend.position = "none")

3.5 Did COVID significantly impact my practice time?

graph

Despite a similar median, we can see that there was a reduction towards the 75th percentile of my practice sessions. We can test if this was a significant impact with a t-test.

covid_start <- as.Date("2020/03/23")

inference <- raw_data%>%
  filter(Source != "Estimated")%>%
  mutate(Covid_Status = as.factor(ifelse(Date_Start < covid_start, "Before COVID", "After COVID")),
         Covid_Status = reorder(Covid_Status, desc(Covid_Status)))%>%
  group_by(Covid_Status, Date_Start)%>%
  summarise(Duration = sum(Duration))%>%
  ungroup()
  
  ggplot(inference, aes(Covid_Status, Duration, fill = Covid_Status))+
  geom_boxplot(varwidth = TRUE, col = "black")+
  labs(x = NULL,
       y = "Average practice session (min)")+
  scale_color_tron()+
  scale_fill_tron()+
  theme_ipsum_es()+
  theme(legend.position = "none")

skewness assumption

Given the extremely low p-value, the Shapiro-Wilk normality test implies that the distribution of the data is significantly different from a normal distribution and that we cannot assume the normality. However, we’re working with the entire population dataset for each class and thus, unlike the independence of data, this assumption is not crucial.

  inference %>% 
  select(Covid_Status, Duration) %>% 
  group_by(group = as.character(Covid_Status)) %>%
  do(tidy(shapiro.test(.$Duration)))%>%
  kbl(caption = "Shapiro-Wilk normality test")%>%
  kable_paper("hover", full_width = F)
Shapiro-Wilk normality test
group statistic p.value method
After COVID 0.9607325 3e-07 Shapiro-Wilk normality test
Before COVID 0.9549818 0e+00 Shapiro-Wilk normality test

equal variances assumption

We can see that with a large p value, we should fail to reject the Null hypothesis (Ho) and conclude that we do not have evidence to believe that population variances are not equal and use the equal variances assumption for our t test

tidy(leveneTest(inference$Duration~inference$Covid_Status))%>%
  kbl(caption = "Levene's test")%>%
  kable_paper("hover", full_width = F)
Levene’s test
statistic p.value df df.residual
0.0410026 0.8395891 1 732

t-test

My practice sessions After COVID are significantly shorter than those before the pandemic. This might be surprising, given that we were in the UK most of the time. However, I’ve been spending my time doing a few other things such as improving my technical skillset with R (this analysis wouldn’t have been possible otherwise) and learning italian.

t_test <- inference%>%
  t_test(Duration ~ Covid_Status, var.equal = TRUE)%>%
  add_significance()%>%
  kbl()%>%
  kable_paper("hover", full_width = F)

t_test
.y. group1 group2 n1 n2 statistic df p p.signif
Duration Before COVID After COVID 433 301 3.319481 732 0.000947 ***

3.6 What type of music do I tend to play?

by genre

#write these as a function
#remove axis
raw_data%>%
  group_by(Genre)%>%
  summarise(Duration = as.integer(sum(Duration)/60))%>%
  mutate(Genre = reorder(Genre, Duration))%>%
  arrange(desc(Duration))%>%
  filter(Genre %notin% c("Other", "Not applicable"))%>%
  head(10)%>%
  
  ggplot(aes(Genre, Duration, fill = Duration))+
  geom_col(show.legend = FALSE, col = "black", width = 1)+
  geom_text(aes(label = Duration), show.legend = FALSE, nudge_y = 25)+
  scale_fill_gradient(low="yellow", high="red")+
  labs(x = NULL,
       y = "Total hours of practice",
       subtitle = "*Not applicable* - unclassified practice and *Other* - sight reading + technique practice")+
  coord_flip()+
  theme_ipsum_es()

by composer

raw_data%>%
  filter(Composer != "Not applicable")%>%
  group_by(Composer)%>%
  summarise(Duration = as.integer(sum(Duration)/60))%>%
  mutate(Composer = reorder(Composer, Duration))%>%
  arrange(desc(Duration))%>%
  head(10)%>%
  
  ggplot(aes(Composer, Duration, fill = Duration))+
  geom_col(show.legend = FALSE, col = "black", width = 1)+
  geom_text(aes(label = Duration), show.legend = FALSE, nudge_y = 6)+
  scale_fill_gradient(low="yellow", high="red")+
  labs(x = NULL,
       y = "Total hours of practice",
       subtitle = "*Not applicable* - unclassified practice and *Other* - sight reading + technique practice")+
  coord_flip()+
  theme_ipsum_es()

by piece

raw_data%>%
  group_by(Project)%>%
  summarise(Duration = as.integer(sum(Duration)/60))%>%
  mutate(Project = reorder(Project, Duration))%>%
  arrange(desc(Duration))%>%
  filter(Project %notin% c("Technique", "General", "Sightreading"))%>%
  head(15)%>%
  
  ggplot(aes(Project, Duration, fill = Duration))+
  geom_col(show.legend = FALSE, col = "black", width = 1)+
  geom_text(aes(label = Duration), show.legend = FALSE, nudge_y = 2)+
  scale_fill_gradient(low="yellow", high="red")+
  labs(x = NULL,
       y = "Total hours of practice",
       title = "Top 15 pieces by hours of practice")+
  coord_flip()+
  theme_ipsum_es()

3.7 Table of repertoire so far

# think about gtable?
# add stuff (links to videos)
# (classify as green/red/yellow categories) and link to video where possible

model_data%>%
  select(-Days_Practiced, Standard, -Length)%>%
  mutate(Duration = round(Duration))%>%
  arrange(desc(Date_Start))%>%
  
  kbl(escape = FALSE,
      caption = "test")%>%
  kable_paper(c("hover", "striped"), full_width = F)%>%
  column_spec(c(1,3), bold = T, color = "black")%>%
  scroll_box(height = "450px")
test
Project Genre Duration Date_Start Date_End ABRSM Standard Level Cumulative_Duration
Elton John - Rocket man Modern 47 2020-12-08 2021-04-15 7 Performance Advanced 46.600000
Schumann - Träumerei Romantic 14 2020-11-09 2020-12-05 7 Average Advanced 14.200000
Mozart - Allegro (3rd movement) K282 Classical 28 2020-11-05 2021-04-18 6 Average Intermediate 27.566667
Ibert - Sérénade sur l’eau Modern 10 2020-09-24 2020-10-27 6 Performance Intermediate 10.383333
Kuhlau - Rondo Vivace Classical 24 2020-08-03 2020-10-27 6 Average Intermediate 24.066667
C. Hartmann - The little ballerina Romantic 21 2020-07-14 2020-10-27 6 Performance Intermediate 21.066667
Schumann - Lalling Melody Romantic 5 2020-06-28 2020-08-14 1 Average Beginner 4.633333
Schumann - Melody Romantic 4 2020-06-20 2020-07-22 1 Average Beginner 4.066667
Clementi - Sonatina no 3 - Mov 2 Classical 3 2020-06-04 2020-06-24 1 Performance Beginner 3.116667
Clementi - Sonatina no 3 - Mov 3 Classical 20 2020-06-04 2020-07-11 4 Performance Beginner 20.333333
Chopin - Waltz in Fm Romantic 27 2020-04-18 2020-10-27 6 Performance Intermediate 27.416667
Clementi - Sonatina no 3 - Mov 1 Classical 30 2020-04-07 2020-06-05 4 Performance Beginner 29.533333
Schumann - Kinderszenen 1 Romantic 10 2020-03-25 2020-04-18 5 Average Intermediate 9.866667
Bach - Prelude in G from Cello Suite No 1 Baroque 25 2020-02-04 2020-04-10 5 Average Intermediate 24.733333
Georg Böhm - Minuet in G Baroque 7 2020-01-27 2020-04-18 1 Average Beginner 6.516667
Bach - Invention 4 in Dm Baroque 21 2020-01-25 2020-03-26 5 Performance Intermediate 20.666667
Chopin - Contredanse in Gb Romantic 23 2020-01-16 2020-03-21 6 Performance Intermediate 22.916667
Bach - Minuet in Gm - 115 Baroque 7 2020-01-07 2020-02-01 1 Average Beginner 6.833333
Elton John - Your song (Arr Cornick) Modern 36 2019-11-21 2020-02-07 5 Performance Intermediate 35.683333
Poulenc - Valse Tyrolienne Modern 17 2019-09-02 2019-11-07 5 Performance Intermediate 16.800000
Bach - Prelude in Cm - 934 Baroque 25 2019-08-15 2019-09-29 1 Performance Beginner 24.950000
Schumann - Volksliedchen Romantic 10 2019-07-01 2019-07-28 2 Average Beginner 9.750000
Haydn - Andante in A Classical 39 2019-06-08 2019-11-07 5 Average Intermediate 39.033333
Schumann - Remembrance Romantic 34 2019-04-28 2019-11-07 5 Performance Intermediate 34.050000
Chopin - Waltz in Am Romantic 26 2019-01-07 2019-11-25 4 Performance Beginner 26.116667

3.8 Relation between difficulty and number of practice hours

I define them as beginner, intermediate and advanced between the 8 grades. In reality, there are diploma, equivalent to university degrees but that’s beyond the scope of the analysis (worthwhile returning in 5 years).

ABRSM grade

Simplified, ABBRSM grades are a group of 8 graded exams based on their difficulty (1 - beginner to 8 - advanced). There’s also diploma grades but those are extremely advanced, equivalent of university level studies and out of the scope of this analysis.

More information can be found on their official website at https://gb.abrsm.org/en/exam-support/your-guide-to-abrsm-exams/

model_data%>%
  mutate(Duration = Duration)%>%
  
  ggplot(aes(ABRSM, Duration, fill = ABRSM))+
  geom_boxplot(outlier.shape = NA)+
  geom_jitter(width = 0.1, height = 0.1, alpha = 0.5)+
  labs(x = "ABRSM Grade",
       y = "Total practice hours")+
  scale_color_tron()+
  scale_fill_tron()+
  theme_ipsum_es()+
  theme(legend.position = "none")

level

A further aggregration of Grades; this is helpful given the very limited dataset. This is an oversimplification but they’re classified as: * 1-5: Beginner (1) * 5-6: Intermediate (2) * 7-8: Advanced (3)

model_data%>%
  mutate(Duration = Duration)%>%
  
  ggplot(aes(Level, Duration, fill = Level))+
  geom_boxplot(aes(outlier.shape = NA))+
  geom_jitter(width = 0.2, height = 0.2)+
  scale_color_tron()+
  scale_fill_tron()+
  labs(x = "Level",
       y = "Total practice hours")+
  theme_ipsum_es()+
  theme(legend.position = "none")

Learning effect - do pieces of the same difficulty become easier to learn with time?

We can spot a trend where the time required to learn a piece of a similar difficulty (ABRSM Grade) decreases as my ability to play the piano increases (as judged by cumulative hours of practice), for ABRSM grades with a significant sample size. We should keep this in mind and include it as a variable into our prediction model.

model_data%>%
  
  ggplot(aes(Cumulative_Duration, Duration, col = Level))+
  geom_point()+
  geom_smooth(method = "lm", se=FALSE)+
  facet_wrap(.~ABRSM)+
  labs(x = "Cumulative hours practiced",
       y = "Hours needed to learn a piece")+
  scale_color_tron()+
  scale_fill_tron()+
  theme_ipsum_es()+
  theme(legend.position = "none")

3.9 What impact does coming back to a piece have?

How do we differentiate between pieces that we learn once and those that we come back to repeatedly? Examples could include wanting to improve the playing further, loving it so much we wanted to relearn it, preparing it for a new performance, etc.

As anyone that ever played the piano knows, re-learning a piece, particularly after you “drop” it for a few months/years, results in a much better performance/understanding of the piece. I definitely found that to be true in my experience, particularly with my exam pieces.

The downside (as it comes to modelling an algorithm) is that these pieces take longer to learn. Not only that you play a piece past the point where you “learn’t” it but not playing a piece for a while will result in losing the skill required to play it. As a result, you both have to “catch up” to where you were as well as supplement past that point. If we don’t include this as a variable in our model, some pieces will show up as requiring more time to learn than required.

Knowing my practice habits/myself, I never practice a piece for more than 3-4 months without a longer break in between so I chose 130 days as a “delimiter”. Looking at those outside this range, I can see that those highlighter were all pieces I came back to rather than pieces I practiced continuously. Unsurprisingly, they are placed towards the higher end of total time practiced/piece of each grade.

This is a simplification of the whole aspect (people literally have PhDs on the topic of spatial repetition) so it cannot be all summarised in a simple variable. However, I thought this simplified version will be better than nothing.

model_data%>%
  mutate(Project_formatted = str_replace_all(Project,"[^[:graph:]]", " "),
         Project_label = ifelse(Days_Practiced > 130, Project_formatted, ""))%>%
  
  ggplot(aes(Days_Practiced, Duration, col = Days_Practiced > 130))+
  geom_point()+
  geom_vline(xintercept = 130, col = "gray", lty = "dashed")+
  geom_smooth(method = "lm", se=FALSE)+
  geom_text_repel(aes(label = Project_label), size = 3)+
  facet_wrap(.~ABRSM)+
  scale_color_tron()+
  scale_fill_tron()+
  labs(x = "Days passed since I the first time I started a piece (to the last practice session)",
       y = "Hours needed to learn a piece")+
  theme_ipsum_es()+
  theme(legend.position = "none")

4 Modelling

Question: How long would it take to practice a piece based on various factors?

4.1 detect outliers

This is particularly important given the small dataset, where one outlier could significantly impact the models.

# graph the other variables in the model above?
model_data%>%

  mutate(Duration2 = mean(Duration),
         mx = Duration2 - Duration)%>%
  ggplot(aes(mx))+
  geom_histogram()

# to find boxplot, scatterplot, z-score, IQR score


# to handle: cap at some treshold, transformations to reduce skewness (BoxCox), remove outliers only if they are anomalies or errors


# remove Elton John, remove Bach (restudied but only tracked the second time, remove Clementi extremely small with repeat)

4.2 missing values

The dataset does not have any missing values. The pieces that did not have an official ABRSM grading were given one based on searching for opinions online and consulting with my teacher.

4.3 Feature engineering

  • numerical:
    • feature for hours practiced by that point for each piece
    • length - based on my own recordings since they’re most directly relatable to my own practice (ie. practicing for repeats, usually takes longer due to different interpretations)
  • categorical:
    • Grade - ABRSM grade
    • Level - a further aggregration of Grades; this is helpful given the very limited dataset. This is an oversimplification but they’re classified as
#regVar <- c("Days_Practiced", "Length", "Cumulative_Duration")

# featurePlot(x = model_data[, regVar], 
#             y = model_data$Duration, 
#             col = model_data$Level,
#             plot = "scatter",
#             type = c("p", "smooth"),
#             span = .5,
#             layout = c(3, 1))
# nearZeroVar(model_data)

# categorical variable can be seen as continuous only when it is ordinal in nature
# max time between two different sessions on the same piece exceeds a limit
#binarisation of data (ie. Standard)
# VIF formulticollinearity
# regularisation

4.4 Pre-processing

Let’s use some basic standardisation offered by the caret package such as centering (subtract mean from values) and scaling (divide values by standard deviation).

4.5 Resampling

Given the small size of the dataset, bootstrapping resampling method will be applied.

train.control <- trainControl(method = "boot",
                              number = 10,
                              search = "random")

#These variables have zero variances: GenreNot applicable, GenreOther

4.6 Model selection

model_data <- model_data%>%filter(Project != "Elton John - Rocket man")

clusters <- 4

#run them all in paralel
cl <- makeCluster(clusters, type = "SOCK")
 
#register cluster train in paralel
registerDoSNOW(cl)

#train models
model <- train(Duration ~ ABRSM + Genre + Length + Cumulative_Duration + ifelse(Days_Practiced>130, 2, 1) + Standard,
                  data = model_data,
                  method = "ranger",
                  preProcess = c("center", "scale", "BoxCox"),
                  tuneLength = 100,
                  trControl = train.control)


model2 <- train(Duration ~ ABRSM + Genre + Length + Cumulative_Duration + ifelse(Days_Practiced>130, 2, 1) + Standard,
                data = model_data,
                method = "lmStepAIC",
                preProcess = c("center", "scale", "BoxCox"),
                tuneLength = 100,
                trControl = train.control)


model3 <- train(Duration ~ ABRSM + Genre + Length + Cumulative_Duration + ifelse(Days_Practiced>130, 2, 1) + Standard,
                data = model_data,
                method = "lm",
                preProcess = c("center", "scale", "BoxCox"),
                tuneLength = 100,
                trControl = train.control)

model4 <- train(Duration ~ ABRSM + Genre + Length + Cumulative_Duration + ifelse(Days_Practiced>130, 2, 1) + Standard,
                data = model_data,
                method = "gbm",
                preProcess = c("center", "scale", "BoxCox"),
                tuneLength = 100,
                trControl = train.control)

model5 <- train(Duration ~ ABRSM + Genre + Length + Cumulative_Duration + ifelse(Days_Practiced>130, 2, 1) + Standard,
                data = model_data,
                method = "rf",
                preProcess = c("center", "scale", "BoxCox"),
                tuneLength = 100,
                trControl = train.control)

model6 <- train(Duration ~ ABRSM + Genre + Length + Cumulative_Duration + ifelse(Days_Practiced>130, 2, 1) + Standard,
                data = model_data,
                method = "gbm",
                preProcess = c("center", "scale", "BoxCox"),
                tuneLength = 100,
                trControl = train.control)

#repeat
model7 <- train(Duration ~ ABRSM + Genre + Length + Cumulative_Duration + Days_Practiced + Standard,
                data = model_data,
                method = "gbm",
                preProcess = c("center", "scale", "BoxCox"),
                tuneLength = 100,
                trControl = train.control)
 
#shut the instances of R down
stopCluster(cl)

#compare models
model_list <- list(one = model, two = model2, three = model3, four = model4, five = model5, six = model6, seven = model7)

model_comparison <- resamples(model_list)

summary(model_comparison)

# Estimate accuracy based on different groups? why does the model perform badly there
# keep LM model for explanation or even RF

# correlation
# learning curves to indicate overfitting and underfitting

# transform days_practiced into something more like 1-2-3 based on 120 days? why is chopin so high 

# hyper parameters 
# https://topepo.github.io/caret/model-training-and-tuning.html#model-training-and-parameter-tuning
# https://topepo.github.io/caret/random-hyperparameter-search.html

We chose the Random Forest model as it was the best performing model. It is known as a model which is:

  • not very sensitive to outliers
  • good for non-linearity
  • variable importance can be biased if categorical variables have few levels (toward high levels) or are correlated

4.6.1 Actuals vs Predictions

selected_model <- model5

#Saving the model
saveRDS(selected_model, file = "model.rda")

#get predictions
predictions <- predict(selected_model, model_data)

#create dataset
model_data2 <- model_data
model_data2$Predicted <- predictions
model_data2$Actual <- model_data$Duration
model_data2$Residuals <- model_data2$Actual - model_data2$Predicted

# model_data2 <- model_data%>%
#   mutate(Actual = as.numeric(Duration),
#          Predicted = as.numeric(predictions),
#          Residuals = Actual - Predicted)%>%
#   select(Predicted, Actual, Residuals, Project, Level, Genre)

#visualise predicted vs actual
ggplotly(
ggplot(model_data2, aes(Predicted, Actual, label = Residuals, col = Level))+
  geom_point(aes(text = Project))+
  geom_smooth(method = "loess", col = "red", lwd = 1, se = FALSE)+
  geom_abline(lty = "dashed", lwd = 0.5, col = "gray")+
  coord_cartesian(xlim = c(0,50), ylim = c(0,50))+
  labs(col = NULL)+
  scale_color_tron()+
  theme_ipsum_es() +
  theme(legend.position = "top")
) %>%
  layout(legend = list(
      orientation = "h",
      x = 0.4, y = 1.2))

4.6.2 Residual distribution

We can see that the residuals are mostly situated around 0.

ggplot(model_data2, aes(Residuals, fill = ..count..))+
  geom_histogram(binwidth = 1, col = "black")+
  geom_vline(aes(xintercept=mean(Residuals)), lwd = 1, lty = 2) +
  labs(x="Residuals",
       y= "Total occurences")+
  scale_fill_gradient(low="yellow", high="red")+
  theme_ipsum_es()+
  theme(legend.position = "none")

4.6.3 Actual versus Residuals

Lastly, we can see that there is a constant variability of errors. However, there is still a tendency to underpredict for pieces that took very little and over predict required time for pieces that took longer than necessary.

ggplotly(
ggplot(model_data2, aes(Actual, Residuals, col = Level, label = Predicted))+
  geom_hline(yintercept = 0, size = 3, color = "grey52")+
  geom_point(aes(text = Project), alpha = 0.5)+
  geom_smooth(method = "loess", col = "red", se = FALSE)+
  labs(col = NULL)+
  scale_color_tron()+
  theme_ipsum_es()
) %>%
  layout(legend = list(orientation = "h",x = 0.4, y = 1.2))

4.7 Which model performed better?

tidy(compare_models(model4, model5))%>%
  kbl(caption = "Model 1 vs model 2")%>% #change this
  kable_paper("hover", full_width = F)
Model 1 vs model 2
estimate statistic p.value parameter conf.low conf.high method alternative
1.546428 2.879039 0.0182065 9 0.3313476 2.761508 One Sample t-test two.sided

These results also confirm that the Random Forrest model is significantly better than the other two.

4.8 How many predictors did the most optimal model have?

plot(model5, 
     main = "The most optimal model was that with 5 predictors", col = "orange", lwd = 3)

4.9 What were the most important variables?

imp <- varImp(model5)

ggplot(imp, size = 6)+
  geom_col(col = "white", fill = "white")+
  geom_segment(aes(Feature, y = 0, xend = Feature, yend = Importance), col = "black", size = 1) +
  geom_point(size = 8, col = "orange")+
  geom_text(aes(label = paste(round(Importance), "%", sep = "")), color = "black", size = 3, check_overlap = TRUE)+
  scale_color_tron()+
  scale_fill_tron()+
  theme_ipsum_es()+
  labs(title = "Variable importance ranking")+
  theme(axis.text.x =  element_blank(), 
        axis.ticks = element_blank())

#compare with linear regression

5 Limitations

6 Application:

7 Hardest things about this analysis:

8 Summary / what did I learn?

What’s next?

9 Other

LS0tDQogICAgb3V0cHV0Og0KICAgICAgaHRtbF9kb2N1bWVudDoNCiANCiAgICAgICAgdG9jOiB0cnVlDQogICAgICAgIHRvY19mbG9hdDogZmFsc2UNCiAgICAgICAgdG9jX2RlcHRoOiAyDQogICAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgICAgICANCiAgICAgICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICAgICAgDQogICAgICAgIGZpZ193aWR0aDogOSANCiAgICAgICAgZmlnX2hlaWdodDogNQ0KICAgICAgICBmaWdfYWxpZ246ICJjZW50ZXIiDQogICAgICAgIA0KICAgICAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgICAgICANCiAgICB0aXRsZTogIkh1bWFuIExlYXJuaW5nIG1lZXRzIE1hY2hpbmUgTGVhcm5pbmciDQogICAgc3VidGl0bGU6ICIxLDIwMCsgaG91cnMgb2YgcGlhbm8gcHJhY3RpY2UiDQogICAgYXV0aG9yOiAiYnkgUGV0ZXIgSG9udGFydSINCiAgICBkYXRlOiAiMTIvMDQvMjAyMCAocmV2aXNlZCkiDQotLS0NCg0KIyBXaHkgdGhlIHByb2plY3Q/DQoNCkNvbnRleHQ6DQoNCiogc3RhcnRlZCBwbGF5aW5nDQotLS0tLSAqIHNob3cgYSB2aWRlbyBvZiBteSBmaXJzdCB5ZWFyIHByb2dyZXNzIHRoZW4gbWF5YmUgYSB2aWRlbyBvZiB0b2RheT8NCg0KV2hhdCBJIGFtIGhvcGluZyB0byBhY2hpZXZlIHdpdGggdGhlIHByb2plY3Q/DQoNCiogdGhpcyBwcm9qZWN0IHdvdWxkIGFsbG93IG1lIHRvIHVuZGVyc3RhbmQgaG93IGxvbmcgaXQgbWlnaHQgdGFrZSBtZSB0byBsZWFybiBhIHBpZWNlIGluIHRoZSBmdXR1cmUgYmFzZWQgb24gcHJldmlvdXMgcGVyZm9ybWFuY2UNCiogc2ltaWxhcmx5LCBpdCBtaWdodCBoZWxwIG90aGVycyBzZWxlY3Qgd2hhdCBwaWVjZXMgdG8gbGVhcm4gYnkgcHJvdmlkaW5nIGEgcmVhbGlzdGljIHRpbWVsaW5lDQoqIGF0IHRoZSBzYW1lIHRpbWUsIGl0IHdpbGwgaG9wZWZ1bGx5IGxldCBvdGhlcnMga25vdyBvZiBuZXcgcGllY2VzIHRoZXkgbWlnaHQgZW5qb3kgbGVhcm5pbmcsIGFjdGluZyBhcyBhIHJlY29tbWVuZGVyIHRvb2wNCiogbGFzdGx5LCBpdCB3aWxsIGhlcGwgcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIG15IHByYWN0aWNlIGhhYml0cyBhbmQgaW5mb3JtIG9uIHdoYXQgSSBuZWVkIHRvIGltcHJvdmUgbmV4dA0KKiBzZWNvbmRhcnkgYW5hbHlzZXMgaW50byB0aGUgbGVhcm5pbmcgZWZmZWN0IChkbyBJIGxlYXJuIGEgcGllY2Ugb2YgdGhlIHNhbWUgZGlmZmljdWx0eSBmYXN0ZXIgYXMgbXkgc2tpbGwgaW1wcm92ZXM/KSANCiogbGFzdGx5LCBidXQgbm90IGxlYXN0LCBpdCB3aWxsIGhvcGVmdWxseSBhY3QgYXMgaW5zcGlyYXRpb24gb2YgYSByZWFsaXN0aWMgcHJvZ3Jlc3Npb24gb2YgYSBwaWFubyBqb3VybmV5IGZvciBvdGhlcnMgd2hvIHdhbnQgdG8gbGVhcm4gYSBtdXNpY2FsIGluc3RybWVudCBhcyBhbiBhZHVsdA0KDQpLZXkgcG9pbnRzOg0KDQoqIGlkZW50aWZpZWQgdmFyaW91cyB0cmVuZHMgYmFzZWQgb24gbXkgcHJhY3RpY2UNCiogcHJlZGljdGVkIGFuIGFsZ29yaXRobQ0KKiB0aGUgYWxnb3JpdGhtIHdhcyBwcm9kdWN0aW9uaXNlZCBpbiBhIHNoaW55IGFwcCBhdCB0aGUgZm9sbG93aW5nIHBhZ2UNCg0KIyBEYXRhIGNvbGxlY3Rpb24NCg0KKiBpbnB1dHRlZCB2ZXJ5IGNvbnNlcnZhdGl2ZSBlc3RpbWF0aW9ucyBmb3IgdGhlIGZpcnN0IDEwIG1vbnRocyBvZiBmaXJzdCB5ZWFyIChKYW4gJzE4IHRvIE9jdCAnMTgpDQoqIE5vdiAnMTggd2FzIHRyYWNrZWQgaW4gYSBzcHJlYWRzaGVldA0KKiBldmVyeXRoaW5nIGZyb20gRGVjICcxOCBvbndhcmRzIHdhcyB0cmFja2VkIHVzaW5nIFRvZ2dsLCBhIHRpbWUtdHJhY2tpbmcgYXBwL3Rvb2wNCiogaW5jbHVkZXMgcHJhY3RpY2Ugb25seSAoSSByZWFsbHkgbG9nZ2VkIGV2ZXJ5dGhpbmcpOyAgZXhjbHVkZXMgcGlhbm8gbGVzc29ucyB1c3VhbGx5IDItMyBob3VycyB0b3RhbCBwZXIgbW9udGgNCiogdGhlIGNvbGxlY3Rpb24vY2xlYW5pbmcgc2NyaXB0IGlzIGF2YWlsYWJsZSBpbiB0aGUgZ2xvYmFsLlIgZmlsZSBvZiB0aGlzIHJlcG8NCg0KIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKiogYWRkIGEgc2NyZWVuc2hvdCBvZiB0aGUgYXBwICoqDQoNCioqRGlzY2xhaW1lcioqOiBJIGFtIGluIG5vIHNoYXBlIG9yIGZvcm0gYWZmaWxpYXRlZCB3aXRoIFRvZ2dsLiBJIHN0YXJ0ZWQgdXNpbmcgaXQgYSBmZXcgeWVhcnMgYWdvIGJlY2F1c2UgSSBsb3ZlZCBpdHMgbWluaW1hbGlzdGljIGRlc2lnbiBhbmQgeWV0IGl0IHByb3ZpZGVkIGFsbCB0aGUgZnVuY3Rpb25hbGl0eSBJIG5lZWRlZC4gSSBhbSB1c2luZyBpdCBvbiBhIGZyZWUgbWVtYmVyc2hpcCBiYXNpcy4NCg0KYGBge3J9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogICAgZWNobyA9IFRSVUUsICMgc2hvdyBhbGwgb2YgdGhlIGNvZGUNCiAgICB0aWR5ID0gRkFMU0UsICMgY2xlYW5lciBjb2RlIHByaW50aW5nDQogICAgc2l6ZSA9ICJzbWFsbCIsICMgc21hbGxlciBjb2RlDQogICAgDQogICAgZmlnLnBhdGggPSAiZmlncy8iLCMgd2hlcmUgdGhlIGZpZ3VyZXMgd2lsbCBlbmQgdXANCiAgICBvdXQud2lkdGggPSAiMTAwJSIsDQoNCiAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgd2FybmluZyA9IEZBTFNFDQogICAgKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQojIHJ1biB0aGUgaW1wb3J0L2NsZWFuIHNjcmlwdA0Kc291cmNlKCJnbG9iYWwuUiIpDQpgYGANCg0KIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpDQoNCiMjIFBpYW5vIHByYWN0aWNlIHRpbWVsaW5lDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTE1fQ0KdGltZWxpbmUgPC0gcmF3X2RhdGElPiUNCiAgZ3JvdXBfYnkoTW9udGhfZm9ybWF0KSU+JQ0KICBzdW1tYXJpc2UoVG90YWxfRHVyYXRpb24gPSBzdW0oRHVyYXRpb24pLzYwKSU+JQ0KICBtdXRhdGUoVG90YWxfRHVyYXRpb24yID0gY3Vtc3VtKFRvdGFsX0R1cmF0aW9uKSwNCiAgICAgICAgIG1heCA9IGFzLmludGVnZXIobWF4KFRvdGFsX0R1cmF0aW9uMikpLA0KICAgICAgICAgbWF4ID0gaWZlbHNlKG1heCA+IFRvdGFsX0R1cmF0aW9uMiwgIiIsIG1heCkpDQoNCiAgI2NvcnJlY3QgZXhhbSBkYXRlcw0KICAjY2FuIGJlIGF1dG9tYXRlZCBpZmVsc2U/DQogIA0KICBnZ3Bsb3QodGltZWxpbmUsIGFlcyhNb250aF9mb3JtYXQsIFRvdGFsX0R1cmF0aW9uMiwgZ3JvdXAgPSAxKSkrDQogIGdlb21fbGluZShzaXplID0gMiwgY29sb3IgPSAiIzY5YjNhMiIpKw0KICBnZW9tX3BvaW50KHNpemUgPSA1LCBjb2xvciA9ICIjNjliM2EyIikrDQogIGdlb21fYXJlYShhbHBoYSA9IDAuMywgZmlsbCA9ICIjNjliM2EyIikrDQogICNncmFkZSAzDQogIGdlb21fcG9pbnQoeD0iT2N0XG4gJzE5IiwgeSA9IDMwMCszOTMuMjgzMzMsIHNpemUgPSA1LCBjb2xvciA9ICJkYXJrIHJlZCIpKw0KICBnZW9tX3RleHQoeD0iT2N0XG4gJzE5IiwgeSA9IDMwMCs0NDMuMjgzMzMsIGxhYmVsID0gIkdyYWRlIDMiKSsNCiAgI2dyYWRlIDUNCiAgZ2VvbV9wb2ludCh4PSJPY3RcbiAnMjAiLCB5ID0gMzAwKzc5NS44NjY2Nywgc2l6ZSA9IDUsIGNvbG9yID0gImRhcmsgcmVkIikrDQogIGdlb21fdGV4dCh4PSJPY3RcbiAnMjAiLCB5ID0gMTE0MC44NjY2NywgIHNpemUgPSA1LCBsYWJlbCA9ICJHcmFkZSA1IikrDQogIGdlb21fdGV4dCh4PSJPY3RcbiAnMjAiLCB5ID0gMzAwKzc0NS44NjY2NywgIHNpemUgPSA1LCBsYWJlbCA9ICI4NDAgaG91cnMiKSsNCiAgIyBOT1cNCiAgZ2VvbV9wb2ludChhZXMoeD0iQXByXG4gJzIxIiwgeSA9IDEyMTkpLCBzaXplID0gNSwgY29sb3IgPSAiZGFyayByZWQiKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG1heCksIG51ZGdlX3kgPSA3NSwgbnVkZ2VfeCA9IC0wLjUsIHNpemUgPSA1KSsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InllbGxvdyIsIGhpZ2g9InJlZCIpKw0KICBsYWJzKHggPSBOVUxMLA0KICAgICAgIHRpdGxlID0gIlBpYW5vIHByYWN0aWNlIHRpbWVsaW5lIikrDQogIHRoZW1lX2lwc3VtX2VzKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KYGBgDQoNCiMjIEFuaW1hdGlvbg0KDQpgYGB7ciBlY2hvPVRSVUUsIGNhY2hlID0gVFJVRX0NCnJhd19kYXRhJT4lDQogIGZpbHRlcihEYXRlX1N0YXJ0ID4gYXMuRGF0ZSgiMjAxOC8xMS8wMSIpKSU+JQ0KICBncm91cF9ieShQcm9qZWN0LCBEYXRlX1N0YXJ0KSU+JQ0KICBzdW1tYXJpc2UoRHVyYXRpb24gPSBzdW0oRHVyYXRpb24pLzYwKSU+JQ0KICBtdXRhdGUoQ3VtdWxhdGl2ZV9QaWVjZSA9IGN1bXN1bShEdXJhdGlvbiksDQogICAgICAgICBNb250aF9ZZWFyID0gYXMuZmFjdG9yKGFzLnllYXJtb24oRGF0ZV9TdGFydCkpLA0KICAgICAgICAgTW9udGhfZm9ybWF0ID0gc3RyX3JlcGxhY2UoTW9udGhfWWVhciwgIiAyMCIsICJcbiAnIikpJT4lDQogIHVuZ3JvdXAoKSU+JQ0KICBtdXRhdGUoQ3VtdWxhdGl2ZV9Ub3RhbCA9IGN1bXN1bShEdXJhdGlvbikpJT4lDQogIGZpbHRlcihQcm9qZWN0ICVub3RpbiUgYygiVGVjaG5pcXVlIiwgIkdlbmVyYWwiLCAiU2lnaHRyZWFkaW5nIikpJT4lDQogIGxlZnRfam9pbihtb2RlbF9kYXRhJT4lc2VsZWN0KExldmVsLCBQcm9qZWN0LCBBQlJTTSksIGJ5ID0gIlByb2plY3QiKSU+JQ0KICANCiNmaXggbGV0dGVyIGlzc3VlIFVUQw0KDQpnZ3Bsb3QoYWVzKERhdGVfU3RhcnQsIEN1bXVsYXRpdmVfUGllY2UsIGZpbGwgPSBMZXZlbCkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMTAsIHNoYXBlID0gMjEsIGNvbCA9ICJibGFjayIsIGFscGhhID0gMC41KSArDQogIHNjYWxlX3NpemUocmFuZ2UgPSBjKC4xLCAxNiksIGd1aWRlID0gRkFMU0UpICsNCiAgI2dlb21fdGV4dChhZXMoeCA9IGFzLkRhdGUoIjIwMjAtMDUtMDEiKSwgeSA9IDQwLCBsYWJlbCA9IE1vbnRoX1llYXIpLCBzaXplID0gMTUsIGNvbG9yID0gJ2xpZ2h0Z3JleScsIGZhbWlseSA9ICdPc3dhbGQnKSArDQogIGxhYnModGl0bGUgPSAnWWVhcjoge2ZyYW1lX3RpbWV9JywNCiAgICAgICB5ID0gIlRvdGFsIHByYWN0aWNlIHRpbWUgcGVyIHBpZWNlIChob3VycykiKSsNCiAgc2NhbGVfY29sb3JfdHJvbigpKw0KICBzY2FsZV9maWxsX3Ryb24oKSsNCiAgdGhlbWVfaXBzdW1fZXMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSsNCiAgdHJhbnNpdGlvbl90aW1lKERhdGVfU3RhcnQpICsNCiAgZWFzZV9hZXMoJ2xpbmVhcicpKw0KICBleGl0X2ZhZGUoKSArDQogIHNoYWRvd19tYXJrKGFscGhhID0gMC4xLCBzaXplID0gNSkNCg0KDQojc2F2ZSBhbmltYXRpb24gYXMgZ2lmIGZvciBsYXRlciB1c2UNCmFuaW1fc2F2ZSgiZmlncy90aW1lbGluZS5naWYiKQ0KDQojbWFrZSBnZW9tcyBwZXJzaXN0IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzYzOTEzMDU5L2lzLXRoZXJlLWEtd2F5LXRvLW1ha2UtZ2VvbXMtZmFkZS1idXQtcGVyc2lzdC1pbi1nZ2FuaW1hdGUNCmBgYA0KDQojIyBIb3cgY29uc2lzdGVudCB3YXMgbXkgcHJhY3RpY2U/DQoNCkdlbmVyYWxseSwgSSd2ZSBkb25lIHByZXR0eSB3ZWxsIHRvIG1haW50YWluIGEgaGlnaCBsZXZlbCBvZiBjb25zaXN0ZW5jeSB3aXRoIHRoZSBleGNlcHRpb24gb2YgQXVndXN0L0RlY2VtYmVyLCB3aGVuZXZlciBJIGdvIG9uIEFubnVhbCBMZWF2ZS4NCg0KYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTV9DQpyYXdfZGF0YSU+JQ0KICBmaWx0ZXIoU291cmNlICE9ICJFc3RpbWF0ZWQiKSU+JQ0KICBncm91cF9ieShNb250aF9ZZWFyLCBNb250aF9TdGFydCwgTW9udGhfZm9ybWF0KSU+JQ0KICBzdW1tYXJpc2UoRGF5c19QcmFjdGljZSA9IG5fZGlzdGluY3QoRGF0ZV9TdGFydCksDQogICAgICAgICAgICBUb3RhbF9EdXJhdGlvbiA9IHN1bShEdXJhdGlvbiwgbmEucm0gPSBUUlVFKSklPiUNCiAgbXV0YXRlKERheXNfVG90YWwgPSBkYXlzX2luX21vbnRoKE1vbnRoX1N0YXJ0KSwNCiAgICAgICAgIERheXNfTm90X1ByYWN0aWNlZCA9IERheXNfVG90YWwgLSBEYXlzX1ByYWN0aWNlLA0KICAgICAgICAgQXZnX0R1cmF0aW9uID0gYXMuaW50ZWdlcihUb3RhbF9EdXJhdGlvbi9EYXlzX1RvdGFsKSwNCiAgICAgICAgIENvbnNpc3RlbmN5ID0gcm91bmQoRGF5c19QcmFjdGljZSAvIERheXNfVG90YWwgKiAxMDAsMiksDQogICAgICAgICBDb25zaXN0ZW5jeV9TdGF0dXMgPSBpZmVsc2UoQ29uc2lzdGVuY3k8NzUsICJCYWQiLCAiR29vZCIpLA0KICAgICAgICAgTW9udGhfZm9ybWF0ID0gcmVvcmRlcihNb250aF9mb3JtYXQsIE1vbnRoX1llYXIpKSU+JQ0KICANCiAgZ2dwbG90KGFlcyhNb250aF9mb3JtYXQsIENvbnNpc3RlbmN5LCBmaWxsID0gQ29uc2lzdGVuY3lfU3RhdHVzKSkrDQogIGdlb21fY29sKGdyb3VwID0gMSwgY29sID0gImJsYWNrIikrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDc1LCBsdHkgPSAiZGFzaGVkIikrDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBEYXlzX05vdF9QcmFjdGljZWQpLCBzaXplID0gNSwgbnVkZ2VfeSA9IDMpKw0KICBsYWJzKHggPSBOVUxMLA0KICAgICAgIGZpbGwgPSAiQ29uc2lzdGVuY3kgc3RhdHVzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJOdW1iZXJzIGluZGljYXRlIGRheXMgd2l0aG91dCBhbnkgcHJhY3RpY2Ugd2l0aGluIGVhY2ggbW9udGgiKSsNCiAgc2NhbGVfY29sb3JfdHJvbigpKw0KICBzY2FsZV9maWxsX3Ryb24oKSsNCiAgdGhlbWVfaXBzdW1fZXMoKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQpgYGANCg0KIyMgV2FzIHRoZXJlIGEgdHJlbmQgaW4gbXkgYW1vdW50IG9mIGRhaWx5IGF2ZXJhZ2UgcHJhY3RpY2U/IHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlc2Ugd2VyZSBjb3JyZWxhdGVkIHdpdGggdGhlIGNvbnNpc3RlbmN5LCB3aGVyZSB0aGUgYXZlcmFnZSBzZXNzaW9uIHdhcyBtdWNoIHNob3J0ZXIgaW4gdGhlIG1vbnRocyBJIHdhcyBhd2F5IGZyb20gdGhlIHBpYW5vLiBUaGVyZSdzIGFsc28gYSB0cmVuZCB3aGVyZSBteSBwcmFjdGljZSBjbG9zZSB0byBhbiBleGFtIHNlc3Npb24gd2FzIHNpZ25pZmljYW50bHkgaGlnaGVyIHRoYW4gYW55IG90aGVyIHRpbWUgb2YgdGhlIHllYXIuICoqQ2FuIHlvdSBzcG90IGluIHdoaWNoIG1vbnRoIEkgaGFkIG15IGV4YW0gaW4gMjAxOT8gV2hhdCBhYm91dCAyMDIwPyoqDQoNCiphdmVyYWdlIHByYWN0aWNlIGxlbmd0aCBwZXIgbW9udGggaW5jbHVkZXMgdGhlIGRheXMgaW4gd2hpY2ggSSBkaWQgbm90IHByYWN0aWNlKg0KDQojIyMgb3ZlcmFsbCB7LX0NCg0KYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTV9DQpyYXdfZGF0YSU+JQ0KICBmaWx0ZXIoU291cmNlICE9ICJFc3RpbWF0ZWQiKSU+JQ0KICBncm91cF9ieShNb250aF9ZZWFyLCBNb250aF9TdGFydCwgTW9udGhfZm9ybWF0KSU+JQ0KICBzdW1tYXJpc2UoRGF5c19QcmFjdGljZSA9IG5fZGlzdGluY3QoRGF0ZV9TdGFydCksDQogICAgICAgICAgICBUb3RhbF9EdXJhdGlvbiA9IHN1bShEdXJhdGlvbikpJT4lDQogIG11dGF0ZShEYXlzX1RvdGFsID0gZGF5c19pbl9tb250aChNb250aF9TdGFydCksDQogICAgICAgICBBdmdfRHVyYXRpb24gPSBhcy5pbnRlZ2VyKFRvdGFsX0R1cmF0aW9uL0RheXNfVG90YWwpLA0KICAgICAgICAgQXZnX0R1cmF0aW9uX1N0YXR1cyA9IGlmZWxzZShBdmdfRHVyYXRpb24gPCA2MCwgIkxlc3MgdGhhbiBvbmUgaG91ciIsICJPbmUgaG91ciIpLA0KICAgICAgICAgTW9udGhfZm9ybWF0ID0gcmVvcmRlcihNb250aF9mb3JtYXQsIE1vbnRoX1llYXIpKSU+JQ0KICANCiAgZ2dwbG90KGFlcyhNb250aF9mb3JtYXQsIEF2Z19EdXJhdGlvbiwgZmlsbCA9IEF2Z19EdXJhdGlvbl9TdGF0dXMpKSsNCiAgZ2VvbV9jb2woY29sID0gImJsYWNrIikrDQogIGxhYnMoeCA9IE5VTEwsDQogICAgICAgeSA9ICJBdmVyYWdlIHByYWN0aWNlIHNlc3Npb24gbGVuZ3RoIChtaW4pIiwNCiAgICAgICBmaWxsID0gIlN0YXR1cyIpKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQXZnX0R1cmF0aW9uKSwgbnVkZ2VfeSA9IDUsIHNpemUgPSA1KSsNCiAgc2NhbGVfY29sb3JfdHJvbigpKw0KICBzY2FsZV9maWxsX3Ryb24oKSsNCiAgdGhlbWVfaXBzdW1fZXMoKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KIyMjIFllYXIgb24gWWVhciB7LX0NCg0KU2ltaWxhciB0cmVuZHMgYXMgYmVmb3JlIGFyZSBhcHBhcmVudCB3aGVyZSBteSBhdmVyYWdlIGRhaWx5IHNlc3Npb24gaXMgbG9uZ2VyIGJlZm9yZSB0aGUgZXhhbXMgdGhhbiBhbnkgb3RoZXIgdGltZSBpbiB0aGUgeWVhciBhbmQgYSBkaXAgaW4gdGhlIG1vbnRocyB3aGVyZSBJIHVzdWFsbHkgdGFrZSBtb3N0IG9mIG15IGFubnVhbCBsZWF2ZS4gSSByZWFsbHkgZG8gbmVlZCB0byBzdGFydCBwaWNraW5nIHVwIHRoZSBwYWNlIGFuZCBnZXQgYmFjayB0byB3aGVyZSBJIHVzZWQgdG8gYmUuDQoNCmBgYHtyfQ0KcmF3X2RhdGElPiUNCiAgZ3JvdXBfYnkoTW9udGhfWWVhciwgTW9udGhfU3RhcnQsIE1vbnRoX2Zvcm1hdCwgTW9udGhfTmFtZSwgWWVhciklPiUNCiAgc3VtbWFyaXNlKERheXNfUHJhY3RpY2UgPSBuX2Rpc3RpbmN0KERhdGVfU3RhcnQpLA0KICAgICAgICAgICAgVG90YWxfRHVyYXRpb24gPSBzdW0oRHVyYXRpb24pKSU+JQ0KICBtdXRhdGUoRGF5c19Ub3RhbCA9IGRheXNfaW5fbW9udGgoTW9udGhfU3RhcnQpLA0KICAgICAgICAgQXZnX0R1cmF0aW9uID0gYXMuaW50ZWdlcihUb3RhbF9EdXJhdGlvbi9EYXlzX1RvdGFsKSwNCiAgICAgICAgIEF2Z19EdXJhdGlvbl9TdGF0dXMgPSBpZmVsc2UoQXZnX0R1cmF0aW9uIDwgNjAsICJMZXNzIHRoYW4gb25lIGhvdXIiLCAiT25lIGhvdXIiKSwNCiAgICAgICAgIE1vbnRoX2Zvcm1hdCA9IHJlb3JkZXIoTW9udGhfZm9ybWF0LCBNb250aF9ZZWFyKSwNCiAgICAgICAgIHNpemUgPSBhcy5mYWN0b3IoaWZlbHNlKFllYXIgPT0gMjAxOCwgMSwgMS41KSksDQogICAgICAgICBsYWJlbCA9IGlmZWxzZShtb250aChNb250aF9TdGFydCkgPT0gMSwgYXMuY2hhcmFjdGVyKFllYXIpLCAiIikpJT4lDQogIA0KICBnZ3Bsb3QoYWVzKE1vbnRoX05hbWUsIEF2Z19EdXJhdGlvbiwgZ3JvdXAgPSBZZWFyLCBzaXplID0gc2l6ZSkpKw0KICBnZW9tX2xpbmUoYWVzKGNvbCA9IFllYXIpKSsNCiAgZ2VvbV9sYWJlbF9yZXBlbChhZXMobGFiZWwgPSBsYWJlbCwgY29sID0gWWVhcikpKw0KICBsYWJzKHggPSBOVUxMLA0KICAgICAgIGZpbGwgPSAiU3RhdHVzIikrDQogIHNjYWxlX2NvbG9yX3Ryb24oKSsNCiAgc2NhbGVfZmlsbF90cm9uKCkrDQogIHRoZW1lX2lwc3VtX2VzKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIyBEaWQgQ09WSUQgc2lnbmlmaWNhbnRseSBpbXBhY3QgbXkgcHJhY3RpY2UgdGltZT8gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBncmFwaCB7LX0NCg0KRGVzcGl0ZSBhIHNpbWlsYXIgbWVkaWFuLCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgd2FzIGEgcmVkdWN0aW9uIHRvd2FyZHMgdGhlIDc1dGggcGVyY2VudGlsZSBvZiBteSBwcmFjdGljZSBzZXNzaW9ucy4gV2UgY2FuIHRlc3QgaWYgdGhpcyB3YXMgYSBzaWduaWZpY2FudCBpbXBhY3Qgd2l0aCBhIHQtdGVzdC4NCg0KYGBge3J9DQpjb3ZpZF9zdGFydCA8LSBhcy5EYXRlKCIyMDIwLzAzLzIzIikNCg0KaW5mZXJlbmNlIDwtIHJhd19kYXRhJT4lDQogIGZpbHRlcihTb3VyY2UgIT0gIkVzdGltYXRlZCIpJT4lDQogIG11dGF0ZShDb3ZpZF9TdGF0dXMgPSBhcy5mYWN0b3IoaWZlbHNlKERhdGVfU3RhcnQgPCBjb3ZpZF9zdGFydCwgIkJlZm9yZSBDT1ZJRCIsICJBZnRlciBDT1ZJRCIpKSwNCiAgICAgICAgIENvdmlkX1N0YXR1cyA9IHJlb3JkZXIoQ292aWRfU3RhdHVzLCBkZXNjKENvdmlkX1N0YXR1cykpKSU+JQ0KICBncm91cF9ieShDb3ZpZF9TdGF0dXMsIERhdGVfU3RhcnQpJT4lDQogIHN1bW1hcmlzZShEdXJhdGlvbiA9IHN1bShEdXJhdGlvbikpJT4lDQogIHVuZ3JvdXAoKQ0KICANCiAgZ2dwbG90KGluZmVyZW5jZSwgYWVzKENvdmlkX1N0YXR1cywgRHVyYXRpb24sIGZpbGwgPSBDb3ZpZF9TdGF0dXMpKSsNCiAgZ2VvbV9ib3hwbG90KHZhcndpZHRoID0gVFJVRSwgY29sID0gImJsYWNrIikrDQogIGxhYnMoeCA9IE5VTEwsDQogICAgICAgeSA9ICJBdmVyYWdlIHByYWN0aWNlIHNlc3Npb24gKG1pbikiKSsNCiAgc2NhbGVfY29sb3JfdHJvbigpKw0KICBzY2FsZV9maWxsX3Ryb24oKSsNCiAgdGhlbWVfaXBzdW1fZXMoKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCiMjIyBza2V3bmVzcyBhc3N1bXB0aW9uIHstfQ0KDQpHaXZlbiB0aGUgZXh0cmVtZWx5IGxvdyBwLXZhbHVlLCB0aGUgU2hhcGlyby1XaWxrIG5vcm1hbGl0eSB0ZXN0IGltcGxpZXMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIGFuZCB0aGF0IHdlIGNhbm5vdCBhc3N1bWUgdGhlIG5vcm1hbGl0eS4gSG93ZXZlciwgd2UncmUgd29ya2luZyB3aXRoIHRoZSBlbnRpcmUgcG9wdWxhdGlvbiBkYXRhc2V0IGZvciBlYWNoIGNsYXNzIGFuZCB0aHVzLCB1bmxpa2UgdGhlIGluZGVwZW5kZW5jZSBvZiBkYXRhLCB0aGlzIGFzc3VtcHRpb24gaXMgbm90IGNydWNpYWwuDQogIA0KYGBge3J9DQogIGluZmVyZW5jZSAlPiUgDQogIHNlbGVjdChDb3ZpZF9TdGF0dXMsIER1cmF0aW9uKSAlPiUgDQogIGdyb3VwX2J5KGdyb3VwID0gYXMuY2hhcmFjdGVyKENvdmlkX1N0YXR1cykpICU+JQ0KICBkbyh0aWR5KHNoYXBpcm8udGVzdCguJER1cmF0aW9uKSkpJT4lDQogIGtibChjYXB0aW9uID0gIlNoYXBpcm8tV2lsayBub3JtYWxpdHkgdGVzdCIpJT4lDQogIGthYmxlX3BhcGVyKCJob3ZlciIsIGZ1bGxfd2lkdGggPSBGKQ0KDQpgYGANCg0KIyMjIGVxdWFsIHZhcmlhbmNlcyBhc3N1bXB0aW9uIHstfQ0KDQpXZSBjYW4gc2VlIHRoYXQgd2l0aCBhIGxhcmdlIHAgdmFsdWUsIHdlIHNob3VsZCBmYWlsIHRvIHJlamVjdCB0aGUgTnVsbCBoeXBvdGhlc2lzIChIbykgYW5kIGNvbmNsdWRlIHRoYXQgd2UgZG8gbm90IGhhdmUgZXZpZGVuY2UgdG8gYmVsaWV2ZSB0aGF0IHBvcHVsYXRpb24gdmFyaWFuY2VzIGFyZSBub3QgZXF1YWwgYW5kIHVzZSB0aGUgZXF1YWwgdmFyaWFuY2VzIGFzc3VtcHRpb24gZm9yIG91ciB0IHRlc3QNCg0KYGBge3J9DQp0aWR5KGxldmVuZVRlc3QoaW5mZXJlbmNlJER1cmF0aW9ufmluZmVyZW5jZSRDb3ZpZF9TdGF0dXMpKSU+JQ0KICBrYmwoY2FwdGlvbiA9ICJMZXZlbmUncyB0ZXN0IiklPiUNCiAga2FibGVfcGFwZXIoImhvdmVyIiwgZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KIyMjIHQtdGVzdCB7LX0NCg0KTXkgcHJhY3RpY2Ugc2Vzc2lvbnMgQWZ0ZXIgQ09WSUQgYXJlIHNpZ25pZmljYW50bHkgc2hvcnRlciB0aGFuIHRob3NlIGJlZm9yZSB0aGUgcGFuZGVtaWMuIFRoaXMgbWlnaHQgYmUgc3VycHJpc2luZywgZ2l2ZW4gdGhhdCB3ZSB3ZXJlIGluIHRoZSBVSyBtb3N0IG9mIHRoZSB0aW1lLiBIb3dldmVyLCBJJ3ZlIGJlZW4gc3BlbmRpbmcgbXkgdGltZSBkb2luZyBhIGZldyBvdGhlciB0aGluZ3Mgc3VjaCBhcyBpbXByb3ZpbmcgbXkgdGVjaG5pY2FsIHNraWxsc2V0IHdpdGggUiAodGhpcyBhbmFseXNpcyB3b3VsZG4ndCBoYXZlIGJlZW4gcG9zc2libGUgb3RoZXJ3aXNlKSBhbmQgbGVhcm5pbmcgaXRhbGlhbi4NCg0KYGBge3J9DQp0X3Rlc3QgPC0gaW5mZXJlbmNlJT4lDQogIHRfdGVzdChEdXJhdGlvbiB+IENvdmlkX1N0YXR1cywgdmFyLmVxdWFsID0gVFJVRSklPiUNCiAgYWRkX3NpZ25pZmljYW5jZSgpJT4lDQogIGtibCgpJT4lDQogIGthYmxlX3BhcGVyKCJob3ZlciIsIGZ1bGxfd2lkdGggPSBGKQ0KDQp0X3Rlc3QNCmBgYA0KDQojIyBXaGF0IHR5cGUgb2YgbXVzaWMgZG8gSSB0ZW5kIHRvIHBsYXk/IHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgYnkgZ2VucmUgey19DQoNCmBgYHtyfQ0KI3dyaXRlIHRoZXNlIGFzIGEgZnVuY3Rpb24NCiNyZW1vdmUgYXhpcw0KcmF3X2RhdGElPiUNCiAgZ3JvdXBfYnkoR2VucmUpJT4lDQogIHN1bW1hcmlzZShEdXJhdGlvbiA9IGFzLmludGVnZXIoc3VtKER1cmF0aW9uKS82MCkpJT4lDQogIG11dGF0ZShHZW5yZSA9IHJlb3JkZXIoR2VucmUsIER1cmF0aW9uKSklPiUNCiAgYXJyYW5nZShkZXNjKER1cmF0aW9uKSklPiUNCiAgZmlsdGVyKEdlbnJlICVub3RpbiUgYygiT3RoZXIiLCAiTm90IGFwcGxpY2FibGUiKSklPiUNCiAgaGVhZCgxMCklPiUNCiAgDQogIGdncGxvdChhZXMoR2VucmUsIER1cmF0aW9uLCBmaWxsID0gRHVyYXRpb24pKSsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSwgY29sID0gImJsYWNrIiwgd2lkdGggPSAxKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IER1cmF0aW9uKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgbnVkZ2VfeSA9IDI1KSsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InllbGxvdyIsIGhpZ2g9InJlZCIpKw0KICBsYWJzKHggPSBOVUxMLA0KICAgICAgIHkgPSAiVG90YWwgaG91cnMgb2YgcHJhY3RpY2UiLA0KICAgICAgIHN1YnRpdGxlID0gIipOb3QgYXBwbGljYWJsZSogLSB1bmNsYXNzaWZpZWQgcHJhY3RpY2UgYW5kICpPdGhlciogLSBzaWdodCByZWFkaW5nICsgdGVjaG5pcXVlIHByYWN0aWNlIikrDQogIGNvb3JkX2ZsaXAoKSsNCiAgdGhlbWVfaXBzdW1fZXMoKQ0KYGBgDQoNCiMjIyBieSBjb21wb3NlciB7LX0NCg0KYGBge3J9DQpyYXdfZGF0YSU+JQ0KICBmaWx0ZXIoQ29tcG9zZXIgIT0gIk5vdCBhcHBsaWNhYmxlIiklPiUNCiAgZ3JvdXBfYnkoQ29tcG9zZXIpJT4lDQogIHN1bW1hcmlzZShEdXJhdGlvbiA9IGFzLmludGVnZXIoc3VtKER1cmF0aW9uKS82MCkpJT4lDQogIG11dGF0ZShDb21wb3NlciA9IHJlb3JkZXIoQ29tcG9zZXIsIER1cmF0aW9uKSklPiUNCiAgYXJyYW5nZShkZXNjKER1cmF0aW9uKSklPiUNCiAgaGVhZCgxMCklPiUNCiAgDQogIGdncGxvdChhZXMoQ29tcG9zZXIsIER1cmF0aW9uLCBmaWxsID0gRHVyYXRpb24pKSsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSwgY29sID0gImJsYWNrIiwgd2lkdGggPSAxKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IER1cmF0aW9uKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgbnVkZ2VfeSA9IDYpKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ieWVsbG93IiwgaGlnaD0icmVkIikrDQogIGxhYnMoeCA9IE5VTEwsDQogICAgICAgeSA9ICJUb3RhbCBob3VycyBvZiBwcmFjdGljZSIsDQogICAgICAgc3VidGl0bGUgPSAiKk5vdCBhcHBsaWNhYmxlKiAtIHVuY2xhc3NpZmllZCBwcmFjdGljZSBhbmQgKk90aGVyKiAtIHNpZ2h0IHJlYWRpbmcgKyB0ZWNobmlxdWUgcHJhY3RpY2UiKSsNCiAgY29vcmRfZmxpcCgpKw0KICB0aGVtZV9pcHN1bV9lcygpDQpgYGANCg0KIyMjIGJ5IHBpZWNlIHstfQ0KDQpgYGB7cn0NCnJhd19kYXRhJT4lDQogIGdyb3VwX2J5KFByb2plY3QpJT4lDQogIHN1bW1hcmlzZShEdXJhdGlvbiA9IGFzLmludGVnZXIoc3VtKER1cmF0aW9uKS82MCkpJT4lDQogIG11dGF0ZShQcm9qZWN0ID0gcmVvcmRlcihQcm9qZWN0LCBEdXJhdGlvbikpJT4lDQogIGFycmFuZ2UoZGVzYyhEdXJhdGlvbikpJT4lDQogIGZpbHRlcihQcm9qZWN0ICVub3RpbiUgYygiVGVjaG5pcXVlIiwgIkdlbmVyYWwiLCAiU2lnaHRyZWFkaW5nIikpJT4lDQogIGhlYWQoMTUpJT4lDQogIA0KICBnZ3Bsb3QoYWVzKFByb2plY3QsIER1cmF0aW9uLCBmaWxsID0gRHVyYXRpb24pKSsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSwgY29sID0gImJsYWNrIiwgd2lkdGggPSAxKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IER1cmF0aW9uKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgbnVkZ2VfeSA9IDIpKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ieWVsbG93IiwgaGlnaD0icmVkIikrDQogIGxhYnMoeCA9IE5VTEwsDQogICAgICAgeSA9ICJUb3RhbCBob3VycyBvZiBwcmFjdGljZSIsDQogICAgICAgdGl0bGUgPSAiVG9wIDE1IHBpZWNlcyBieSBob3VycyBvZiBwcmFjdGljZSIpKw0KICBjb29yZF9mbGlwKCkrDQogIHRoZW1lX2lwc3VtX2VzKCkNCmBgYA0KDQojIyBUYWJsZSBvZiByZXBlcnRvaXJlIHNvIGZhcg0KDQpgYGB7cn0NCiMgdGhpbmsgYWJvdXQgZ3RhYmxlPw0KIyBhZGQgc3R1ZmYgKGxpbmtzIHRvIHZpZGVvcykNCiMgKGNsYXNzaWZ5IGFzIGdyZWVuL3JlZC95ZWxsb3cgY2F0ZWdvcmllcykgYW5kIGxpbmsgdG8gdmlkZW8gd2hlcmUgcG9zc2libGUNCg0KbW9kZWxfZGF0YSU+JQ0KICBzZWxlY3QoLURheXNfUHJhY3RpY2VkLCBTdGFuZGFyZCwgLUxlbmd0aCklPiUNCiAgbXV0YXRlKER1cmF0aW9uID0gcm91bmQoRHVyYXRpb24pKSU+JQ0KICBhcnJhbmdlKGRlc2MoRGF0ZV9TdGFydCkpJT4lDQogIA0KICBrYmwoZXNjYXBlID0gRkFMU0UsDQogICAgICBjYXB0aW9uID0gInRlc3QiKSU+JQ0KICBrYWJsZV9wYXBlcihjKCJob3ZlciIsICJzdHJpcGVkIiksIGZ1bGxfd2lkdGggPSBGKSU+JQ0KICBjb2x1bW5fc3BlYyhjKDEsMyksIGJvbGQgPSBULCBjb2xvciA9ICJibGFjayIpJT4lDQogIHNjcm9sbF9ib3goaGVpZ2h0ID0gIjQ1MHB4IikNCmBgYA0KDQojIyBSZWxhdGlvbiBiZXR3ZWVuIGRpZmZpY3VsdHkgYW5kIG51bWJlciBvZiBwcmFjdGljZSBob3VycyB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KSSBkZWZpbmUgdGhlbSBhcyBiZWdpbm5lciwgaW50ZXJtZWRpYXRlIGFuZCBhZHZhbmNlZCBiZXR3ZWVuIHRoZSA4IGdyYWRlcy4gSW4gcmVhbGl0eSwgdGhlcmUgYXJlIGRpcGxvbWEsIGVxdWl2YWxlbnQgdG8gdW5pdmVyc2l0eSBkZWdyZWVzIGJ1dCB0aGF0J3MgYmV5b25kIHRoZSBzY29wZSBvZiB0aGUgYW5hbHlzaXMgKHdvcnRod2hpbGUgcmV0dXJuaW5nIGluIDUgeWVhcnMpLg0KDQojIyMgQUJSU00gZ3JhZGUgey19DQoNClNpbXBsaWZpZWQsIEFCQlJTTSBncmFkZXMgYXJlIGEgZ3JvdXAgb2YgOCBncmFkZWQgZXhhbXMgYmFzZWQgb24gdGhlaXIgZGlmZmljdWx0eSAoMSAtIGJlZ2lubmVyIHRvIDggLSBhZHZhbmNlZCkuIFRoZXJlJ3MgYWxzbyBkaXBsb21hIGdyYWRlcyBidXQgdGhvc2UgYXJlIGV4dHJlbWVseSBhZHZhbmNlZCwgZXF1aXZhbGVudCBvZiB1bml2ZXJzaXR5IGxldmVsIHN0dWRpZXMgYW5kIG91dCBvZiB0aGUgc2NvcGUgb2YgdGhpcyBhbmFseXNpcy4gDQoNCk1vcmUgaW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIG9uIHRoZWlyIG9mZmljaWFsIHdlYnNpdGUgYXQgaHR0cHM6Ly9nYi5hYnJzbS5vcmcvZW4vZXhhbS1zdXBwb3J0L3lvdXItZ3VpZGUtdG8tYWJyc20tZXhhbXMvDQoNCmBgYHtyfQ0KbW9kZWxfZGF0YSU+JQ0KICBtdXRhdGUoRHVyYXRpb24gPSBEdXJhdGlvbiklPiUNCiAgDQogIGdncGxvdChhZXMoQUJSU00sIER1cmF0aW9uLCBmaWxsID0gQUJSU00pKSsNCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSkrDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4xLCBoZWlnaHQgPSAwLjEsIGFscGhhID0gMC41KSsNCiAgbGFicyh4ID0gIkFCUlNNIEdyYWRlIiwNCiAgICAgICB5ID0gIlRvdGFsIHByYWN0aWNlIGhvdXJzIikrDQogIHNjYWxlX2NvbG9yX3Ryb24oKSsNCiAgc2NhbGVfZmlsbF90cm9uKCkrDQogIHRoZW1lX2lwc3VtX2VzKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIyMgbGV2ZWwgey19DQoNCkEgZnVydGhlciBhZ2dyZWdyYXRpb24gb2YgR3JhZGVzOyB0aGlzIGlzIGhlbHBmdWwgZ2l2ZW4gdGhlIHZlcnkgbGltaXRlZCBkYXRhc2V0LiBUaGlzIGlzIGFuIG92ZXJzaW1wbGlmaWNhdGlvbiBidXQgdGhleSdyZSBjbGFzc2lmaWVkIGFzOg0KICAqIDEtNTogQmVnaW5uZXIgKDEpDQogICogNS02OiBJbnRlcm1lZGlhdGUgKDIpDQogICogNy04OiBBZHZhbmNlZCAoMykNCg0KYGBge3J9DQptb2RlbF9kYXRhJT4lDQogIG11dGF0ZShEdXJhdGlvbiA9IER1cmF0aW9uKSU+JQ0KICANCiAgZ2dwbG90KGFlcyhMZXZlbCwgRHVyYXRpb24sIGZpbGwgPSBMZXZlbCkpKw0KICBnZW9tX2JveHBsb3QoYWVzKG91dGxpZXIuc2hhcGUgPSBOQSkpKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMiwgaGVpZ2h0ID0gMC4yKSsNCiAgc2NhbGVfY29sb3JfdHJvbigpKw0KICBzY2FsZV9maWxsX3Ryb24oKSsNCiAgbGFicyh4ID0gIkxldmVsIiwNCiAgICAgICB5ID0gIlRvdGFsIHByYWN0aWNlIGhvdXJzIikrDQogIHRoZW1lX2lwc3VtX2VzKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIyBMZWFybmluZyBlZmZlY3QgLSBkbyBwaWVjZXMgb2YgdGhlIHNhbWUgZGlmZmljdWx0eSBiZWNvbWUgZWFzaWVyIHRvIGxlYXJuIHdpdGggdGltZT8gey19DQoNCldlIGNhbiBzcG90IGEgdHJlbmQgd2hlcmUgdGhlIHRpbWUgcmVxdWlyZWQgdG8gbGVhcm4gYSBwaWVjZSBvZiBhIHNpbWlsYXIgZGlmZmljdWx0eSAoQUJSU00gR3JhZGUpIGRlY3JlYXNlcyBhcyBteSBhYmlsaXR5IHRvIHBsYXkgdGhlIHBpYW5vIGluY3JlYXNlcyAoYXMganVkZ2VkIGJ5IGN1bXVsYXRpdmUgaG91cnMgb2YgcHJhY3RpY2UpLCBmb3IgQUJSU00gZ3JhZGVzIHdpdGggYSBzaWduaWZpY2FudCBzYW1wbGUgc2l6ZS4gV2Ugc2hvdWxkIGtlZXAgdGhpcyBpbiBtaW5kIGFuZCBpbmNsdWRlIGl0IGFzIGEgdmFyaWFibGUgaW50byBvdXIgcHJlZGljdGlvbiBtb2RlbC4NCg0KYGBge3J9DQptb2RlbF9kYXRhJT4lDQogIA0KICBnZ3Bsb3QoYWVzKEN1bXVsYXRpdmVfRHVyYXRpb24sIER1cmF0aW9uLCBjb2wgPSBMZXZlbCkpKw0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKSsNCiAgZmFjZXRfd3JhcCgufkFCUlNNKSsNCiAgbGFicyh4ID0gIkN1bXVsYXRpdmUgaG91cnMgcHJhY3RpY2VkIiwNCiAgICAgICB5ID0gIkhvdXJzIG5lZWRlZCB0byBsZWFybiBhIHBpZWNlIikrDQogIHNjYWxlX2NvbG9yX3Ryb24oKSsNCiAgc2NhbGVfZmlsbF90cm9uKCkrDQogIHRoZW1lX2lwc3VtX2VzKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIyBXaGF0IGltcGFjdCBkb2VzIGNvbWluZyBiYWNrIHRvIGEgcGllY2UgaGF2ZT8NCg0KSG93IGRvIHdlIGRpZmZlcmVudGlhdGUgYmV0d2VlbiBwaWVjZXMgdGhhdCB3ZSBsZWFybiBvbmNlIGFuZCB0aG9zZSB0aGF0IHdlIGNvbWUgYmFjayB0byByZXBlYXRlZGx5PyBFeGFtcGxlcyBjb3VsZCBpbmNsdWRlIHdhbnRpbmcgdG8gaW1wcm92ZSB0aGUgcGxheWluZyBmdXJ0aGVyLCBsb3ZpbmcgaXQgc28gbXVjaCB3ZSB3YW50ZWQgdG8gcmVsZWFybiBpdCwgcHJlcGFyaW5nIGl0IGZvciBhIG5ldyBwZXJmb3JtYW5jZSwgZXRjLg0KDQpBcyBhbnlvbmUgdGhhdCBldmVyIHBsYXllZCB0aGUgcGlhbm8ga25vd3MsIHJlLWxlYXJuaW5nIGEgcGllY2UsIHBhcnRpY3VsYXJseSBhZnRlciB5b3UgImRyb3AiIGl0IGZvciBhIGZldyBtb250aHMveWVhcnMsIHJlc3VsdHMgaW4gYSBtdWNoIGJldHRlciBwZXJmb3JtYW5jZS91bmRlcnN0YW5kaW5nIG9mIHRoZSBwaWVjZS4gSSBkZWZpbml0ZWx5IGZvdW5kIHRoYXQgdG8gYmUgdHJ1ZSBpbiBteSBleHBlcmllbmNlLCBwYXJ0aWN1bGFybHkgd2l0aCBteSBleGFtIHBpZWNlcy4NCg0KVGhlIGRvd25zaWRlIChhcyBpdCBjb21lcyB0byBtb2RlbGxpbmcgYW4gYWxnb3JpdGhtKSBpcyB0aGF0IHRoZXNlIHBpZWNlcyB0YWtlIGxvbmdlciB0byBsZWFybi4gTm90IG9ubHkgdGhhdCB5b3UgcGxheSBhIHBpZWNlIHBhc3QgdGhlIHBvaW50IHdoZXJlIHlvdSAibGVhcm4ndCIgaXQgYnV0IG5vdCBwbGF5aW5nIGEgcGllY2UgZm9yIGEgd2hpbGUgd2lsbCByZXN1bHQgaW4gbG9zaW5nIHRoZSBza2lsbCByZXF1aXJlZCB0byBwbGF5IGl0LiBBcyBhIHJlc3VsdCwgeW91IGJvdGggaGF2ZSB0byAiY2F0Y2ggdXAiIHRvIHdoZXJlIHlvdSB3ZXJlIGFzIHdlbGwgYXMgc3VwcGxlbWVudCBwYXN0IHRoYXQgcG9pbnQuIElmIHdlIGRvbid0IGluY2x1ZGUgdGhpcyBhcyBhIHZhcmlhYmxlIGluIG91ciBtb2RlbCwgc29tZSBwaWVjZXMgd2lsbCBzaG93IHVwIGFzIHJlcXVpcmluZyBtb3JlIHRpbWUgdG8gbGVhcm4gdGhhbiByZXF1aXJlZC4NCg0KS25vd2luZyBteSBwcmFjdGljZSBoYWJpdHMvbXlzZWxmLCBJIG5ldmVyIHByYWN0aWNlIGEgcGllY2UgZm9yIG1vcmUgdGhhbiAzLTQgbW9udGhzIHdpdGhvdXQgYSBsb25nZXIgYnJlYWsgaW4gYmV0d2VlbiBzbyBJIGNob3NlIDEzMCBkYXlzIGFzIGEgImRlbGltaXRlciIuIExvb2tpbmcgYXQgdGhvc2Ugb3V0c2lkZSB0aGlzIHJhbmdlLCBJIGNhbiBzZWUgdGhhdCB0aG9zZSBoaWdobGlnaHRlciB3ZXJlIGFsbCBwaWVjZXMgSSBjYW1lIGJhY2sgdG8gcmF0aGVyIHRoYW4gcGllY2VzIEkgcHJhY3RpY2VkIGNvbnRpbnVvdXNseS4gVW5zdXJwcmlzaW5nbHksIHRoZXkgYXJlIHBsYWNlZCB0b3dhcmRzIHRoZSBoaWdoZXIgZW5kIG9mIHRvdGFsIHRpbWUgcHJhY3RpY2VkL3BpZWNlIG9mIGVhY2ggZ3JhZGUuDQoNClRoaXMgaXMgYSBzaW1wbGlmaWNhdGlvbiBvZiB0aGUgd2hvbGUgYXNwZWN0IChwZW9wbGUgbGl0ZXJhbGx5IGhhdmUgUGhEcyBvbiB0aGUgdG9waWMgb2Ygc3BhdGlhbCByZXBldGl0aW9uKSBzbyBpdCBjYW5ub3QgYmUgYWxsIHN1bW1hcmlzZWQgaW4gYSBzaW1wbGUgdmFyaWFibGUuIEhvd2V2ZXIsIEkgdGhvdWdodCB0aGlzIHNpbXBsaWZpZWQgdmVyc2lvbiB3aWxsIGJlIGJldHRlciB0aGFuIG5vdGhpbmcuDQoNCmBgYHtyIHdpZHRoID0gN30NCm1vZGVsX2RhdGElPiUNCiAgbXV0YXRlKFByb2plY3RfZm9ybWF0dGVkID0gc3RyX3JlcGxhY2VfYWxsKFByb2plY3QsIlteWzpncmFwaDpdXSIsICIgIiksDQogICAgICAgICBQcm9qZWN0X2xhYmVsID0gaWZlbHNlKERheXNfUHJhY3RpY2VkID4gMTMwLCBQcm9qZWN0X2Zvcm1hdHRlZCwgIiIpKSU+JQ0KICANCiAgZ2dwbG90KGFlcyhEYXlzX1ByYWN0aWNlZCwgRHVyYXRpb24sIGNvbCA9IERheXNfUHJhY3RpY2VkID4gMTMwKSkrDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTMwLCBjb2wgPSAiZ3JheSIsIGx0eSA9ICJkYXNoZWQiKSsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpKw0KICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gUHJvamVjdF9sYWJlbCksIHNpemUgPSAzKSsNCiAgZmFjZXRfd3JhcCgufkFCUlNNKSsNCiAgc2NhbGVfY29sb3JfdHJvbigpKw0KICBzY2FsZV9maWxsX3Ryb24oKSsNCiAgbGFicyh4ID0gIkRheXMgcGFzc2VkIHNpbmNlIEkgdGhlIGZpcnN0IHRpbWUgSSBzdGFydGVkIGEgcGllY2UgKHRvIHRoZSBsYXN0IHByYWN0aWNlIHNlc3Npb24pIiwNCiAgICAgICB5ID0gIkhvdXJzIG5lZWRlZCB0byBsZWFybiBhIHBpZWNlIikrDQogIHRoZW1lX2lwc3VtX2VzKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIE1vZGVsbGluZw0KDQpRdWVzdGlvbjogKipIb3cgbG9uZyB3b3VsZCBpdCB0YWtlIHRvIHByYWN0aWNlIGEgcGllY2UgYmFzZWQgb24gdmFyaW91cyBmYWN0b3JzPyoqDQoNCiMjIGRldGVjdCBvdXRsaWVycw0KDQpUaGlzIGlzIHBhcnRpY3VsYXJseSBpbXBvcnRhbnQgZ2l2ZW4gdGhlIHNtYWxsIGRhdGFzZXQsIHdoZXJlIG9uZSBvdXRsaWVyIGNvdWxkIHNpZ25pZmljYW50bHkgaW1wYWN0IHRoZSBtb2RlbHMuDQoNCmBgYHtyfQ0KIyBncmFwaCB0aGUgb3RoZXIgdmFyaWFibGVzIGluIHRoZSBtb2RlbCBhYm92ZT8NCm1vZGVsX2RhdGElPiUNCg0KICBtdXRhdGUoRHVyYXRpb24yID0gbWVhbihEdXJhdGlvbiksDQogICAgICAgICBteCA9IER1cmF0aW9uMiAtIER1cmF0aW9uKSU+JQ0KICBnZ3Bsb3QoYWVzKG14KSkrDQogIGdlb21faGlzdG9ncmFtKCkNCg0KDQojIHRvIGZpbmQgYm94cGxvdCwgc2NhdHRlcnBsb3QsIHotc2NvcmUsIElRUiBzY29yZQ0KDQoNCiMgdG8gaGFuZGxlOiBjYXAgYXQgc29tZSB0cmVzaG9sZCwgdHJhbnNmb3JtYXRpb25zIHRvIHJlZHVjZSBza2V3bmVzcyAoQm94Q294KSwgcmVtb3ZlIG91dGxpZXJzIG9ubHkgaWYgdGhleSBhcmUgYW5vbWFsaWVzIG9yIGVycm9ycw0KDQoNCiMgcmVtb3ZlIEVsdG9uIEpvaG4sIHJlbW92ZSBCYWNoIChyZXN0dWRpZWQgYnV0IG9ubHkgdHJhY2tlZCB0aGUgc2Vjb25kIHRpbWUsIHJlbW92ZSBDbGVtZW50aSBleHRyZW1lbHkgc21hbGwgd2l0aCByZXBlYXQpDQpgYGANCg0KIyMgbWlzc2luZyB2YWx1ZXMNCg0KVGhlIGRhdGFzZXQgZG9lcyBub3QgaGF2ZSBhbnkgbWlzc2luZyB2YWx1ZXMuIFRoZSBwaWVjZXMgdGhhdCBkaWQgbm90IGhhdmUgYW4gb2ZmaWNpYWwgQUJSU00gZ3JhZGluZyB3ZXJlIGdpdmVuIG9uZSBiYXNlZCBvbiBzZWFyY2hpbmcgZm9yIG9waW5pb25zIG9ubGluZSBhbmQgY29uc3VsdGluZyB3aXRoIG15IHRlYWNoZXIuDQoNCiMjIEZlYXR1cmUgZW5naW5lZXJpbmcNCg0KKiAqKm51bWVyaWNhbCoqOg0KICAqIGZlYXR1cmUgZm9yIGhvdXJzIHByYWN0aWNlZCBieSB0aGF0IHBvaW50IGZvciBlYWNoIHBpZWNlDQogICogbGVuZ3RoIC0gYmFzZWQgb24gbXkgb3duIHJlY29yZGluZ3Mgc2luY2UgdGhleSdyZSBtb3N0IGRpcmVjdGx5IHJlbGF0YWJsZSB0byBteSBvd24gcHJhY3RpY2UgKGllLiBwcmFjdGljaW5nIGZvciByZXBlYXRzLCB1c3VhbGx5IHRha2VzIGxvbmdlciBkdWUgdG8gZGlmZmVyZW50IGludGVycHJldGF0aW9ucykNCiogKipjYXRlZ29yaWNhbCoqOg0KICAqIEdyYWRlIC0gQUJSU00gZ3JhZGUNCiAgKiBMZXZlbCAtIGEgZnVydGhlciBhZ2dyZWdyYXRpb24gb2YgR3JhZGVzOyB0aGlzIGlzIGhlbHBmdWwgZ2l2ZW4gdGhlIHZlcnkgbGltaXRlZCBkYXRhc2V0LiBUaGlzIGlzIGFuIG92ZXJzaW1wbGlmaWNhdGlvbiBidXQgdGhleSdyZSBjbGFzc2lmaWVkIGFzDQoNCmBgYHtyfQ0KI3JlZ1ZhciA8LSBjKCJEYXlzX1ByYWN0aWNlZCIsICJMZW5ndGgiLCAiQ3VtdWxhdGl2ZV9EdXJhdGlvbiIpDQoNCiMgZmVhdHVyZVBsb3QoeCA9IG1vZGVsX2RhdGFbLCByZWdWYXJdLCANCiMgICAgICAgICAgICAgeSA9IG1vZGVsX2RhdGEkRHVyYXRpb24sIA0KIyAgICAgICAgICAgICBjb2wgPSBtb2RlbF9kYXRhJExldmVsLA0KIyAgICAgICAgICAgICBwbG90ID0gInNjYXR0ZXIiLA0KIyAgICAgICAgICAgICB0eXBlID0gYygicCIsICJzbW9vdGgiKSwNCiMgICAgICAgICAgICAgc3BhbiA9IC41LA0KIyAgICAgICAgICAgICBsYXlvdXQgPSBjKDMsIDEpKQ0KIyBuZWFyWmVyb1Zhcihtb2RlbF9kYXRhKQ0KDQojIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGNhbiBiZSBzZWVuIGFzIGNvbnRpbnVvdXMgb25seSB3aGVuIGl0IGlzIG9yZGluYWwgaW4gbmF0dXJlDQojIG1heCB0aW1lIGJldHdlZW4gdHdvIGRpZmZlcmVudCBzZXNzaW9ucyBvbiB0aGUgc2FtZSBwaWVjZSBleGNlZWRzIGEgbGltaXQNCiNiaW5hcmlzYXRpb24gb2YgZGF0YSAoaWUuIFN0YW5kYXJkKQ0KIyBWSUYgZm9ybXVsdGljb2xsaW5lYXJpdHkNCiMgcmVndWxhcmlzYXRpb24NCmBgYA0KDQojIyBQcmUtcHJvY2Vzc2luZw0KDQpMZXQncyB1c2Ugc29tZSBiYXNpYyBzdGFuZGFyZGlzYXRpb24gb2ZmZXJlZCBieSB0aGUgY2FyZXQgcGFja2FnZSBzdWNoIGFzICoqY2VudGVyaW5nKiogKHN1YnRyYWN0IG1lYW4gZnJvbSB2YWx1ZXMpIGFuZCAqKnNjYWxpbmcqKiAoZGl2aWRlIHZhbHVlcyBieSBzdGFuZGFyZCBkZXZpYXRpb24pLg0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kc2V0LnNlZWQoMTIzKQ0KDQp5IDwtIG1vZGVsX2RhdGEkRHVyYXRpb24NCg0KI3Rha2Ugb3V0IHByaWNlIHRlbXBvcmFyaWx5IGFzIHdlIGRvIG5vdCB3YW50IHRoaXMgdG8gYmUgcHJvY2Vzc2VkDQptb2RlbF9kYXRhIDwtIG1vZGVsX2RhdGEgJT4lDQogIHNlbGVjdCgtRHVyYXRpb24pDQoNCiNjZW50ZXIgYW5kIHNjYWxlIG91ciBkYXRhDQpwcmVQcm9jZXNzX3JhbmdlX21vZGVsIDwtIHByZVByb2Nlc3MobW9kZWxfZGF0YSwgbWV0aG9kPWMoImNlbnRlciIsICJzY2FsZSIpKQ0KDQptb2RlbF9kYXRhIDwtIHByZWRpY3QocHJlUHJvY2Vzc19yYW5nZV9tb2RlbCwgbmV3ZGF0YSA9IG1vZGVsX2RhdGEpDQogDQojQXBwZW5kIHRoZSBZIHZhcmlhYmxlIGJhY2sgb24gd2l0aCBvcmlnaW5hbCB2YWx1ZXMNCm1vZGVsX2RhdGEkRHVyYXRpb24gPC0geQ0KDQojc2F2ZSB0aGUgdHJhaW4gZGF0YSAodHJhaW5fZGF0YS5jc3YpDQojaWYgdGhlcmUgaXMgc2tld25lc3MgLT4gbG9nLCBzcXVhcmUgcm9vdCBvciBpbnZlcnNlDQpgYGANCg0KIyMgUmVzYW1wbGluZw0KDQpHaXZlbiB0aGUgc21hbGwgc2l6ZSBvZiB0aGUgZGF0YXNldCwgYm9vdHN0cmFwcGluZyByZXNhbXBsaW5nIG1ldGhvZCB3aWxsIGJlIGFwcGxpZWQuDQoNCmBgYHtyfQ0KdHJhaW4uY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImJvb3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWFyY2ggPSAicmFuZG9tIikNCg0KI1RoZXNlIHZhcmlhYmxlcyBoYXZlIHplcm8gdmFyaWFuY2VzOiBHZW5yZU5vdCBhcHBsaWNhYmxlLCBHZW5yZU90aGVyDQpgYGANCg0KIyMgTW9kZWwgc2VsZWN0aW9uDQoNCmBgYHtyIGVjaG89VFJVRSwgY2FjaGUgPSBUUlVFLCByZXN1bHRzPSdoaWRlJ30NCg0KbW9kZWxfZGF0YSA8LSBtb2RlbF9kYXRhJT4lZmlsdGVyKFByb2plY3QgIT0gIkVsdG9uIEpvaG4gLSBSb2NrZXQgbWFuIikNCg0KY2x1c3RlcnMgPC0gNA0KDQojcnVuIHRoZW0gYWxsIGluIHBhcmFsZWwNCmNsIDwtIG1ha2VDbHVzdGVyKGNsdXN0ZXJzLCB0eXBlID0gIlNPQ0siKQ0KIA0KI3JlZ2lzdGVyIGNsdXN0ZXIgdHJhaW4gaW4gcGFyYWxlbA0KcmVnaXN0ZXJEb1NOT1coY2wpDQoNCiN0cmFpbiBtb2RlbHMNCm1vZGVsIDwtIHRyYWluKER1cmF0aW9uIH4gQUJSU00gKyBHZW5yZSArIExlbmd0aCArIEN1bXVsYXRpdmVfRHVyYXRpb24gKyBpZmVsc2UoRGF5c19QcmFjdGljZWQ+MTMwLCAyLCAxKSArIFN0YW5kYXJkLA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGEsDQogICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmFuZ2VyIiwNCiAgICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiLCAiQm94Q294IiksDQogICAgICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMTAwLA0KICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW4uY29udHJvbCkNCg0KDQptb2RlbDIgPC0gdHJhaW4oRHVyYXRpb24gfiBBQlJTTSArIEdlbnJlICsgTGVuZ3RoICsgQ3VtdWxhdGl2ZV9EdXJhdGlvbiArIGlmZWxzZShEYXlzX1ByYWN0aWNlZD4xMzAsIDIsIDEpICsgU3RhbmRhcmQsDQogICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGEsDQogICAgICAgICAgICAgICAgbWV0aG9kID0gImxtU3RlcEFJQyIsDQogICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIsICJCb3hDb3giKSwNCiAgICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMTAwLA0KICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluLmNvbnRyb2wpDQoNCg0KbW9kZWwzIDwtIHRyYWluKER1cmF0aW9uIH4gQUJSU00gKyBHZW5yZSArIExlbmd0aCArIEN1bXVsYXRpdmVfRHVyYXRpb24gKyBpZmVsc2UoRGF5c19QcmFjdGljZWQ+MTMwLCAyLCAxKSArIFN0YW5kYXJkLA0KICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhLA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsDQogICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIsICJCb3hDb3giKSwNCiAgICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMTAwLA0KICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluLmNvbnRyb2wpDQoNCm1vZGVsNCA8LSB0cmFpbihEdXJhdGlvbiB+IEFCUlNNICsgR2VucmUgKyBMZW5ndGggKyBDdW11bGF0aXZlX0R1cmF0aW9uICsgaWZlbHNlKERheXNfUHJhY3RpY2VkPjEzMCwgMiwgMSkgKyBTdGFuZGFyZCwNCiAgICAgICAgICAgICAgICBkYXRhID0gbW9kZWxfZGF0YSwNCiAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2JtIiwNCiAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiwgIkJveENveCIpLA0KICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAxMDAsDQogICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW4uY29udHJvbCkNCg0KbW9kZWw1IDwtIHRyYWluKER1cmF0aW9uIH4gQUJSU00gKyBHZW5yZSArIExlbmd0aCArIEN1bXVsYXRpdmVfRHVyYXRpb24gKyBpZmVsc2UoRGF5c19QcmFjdGljZWQ+MTMwLCAyLCAxKSArIFN0YW5kYXJkLA0KICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhLA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsDQogICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIsICJCb3hDb3giKSwNCiAgICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMTAwLA0KICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluLmNvbnRyb2wpDQoNCm1vZGVsNiA8LSB0cmFpbihEdXJhdGlvbiB+IEFCUlNNICsgR2VucmUgKyBMZW5ndGggKyBDdW11bGF0aXZlX0R1cmF0aW9uICsgaWZlbHNlKERheXNfUHJhY3RpY2VkPjEzMCwgMiwgMSkgKyBTdGFuZGFyZCwNCiAgICAgICAgICAgICAgICBkYXRhID0gbW9kZWxfZGF0YSwNCiAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2JtIiwNCiAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiwgIkJveENveCIpLA0KICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAxMDAsDQogICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW4uY29udHJvbCkNCg0KI3JlcGVhdA0KbW9kZWw3IDwtIHRyYWluKER1cmF0aW9uIH4gQUJSU00gKyBHZW5yZSArIExlbmd0aCArIEN1bXVsYXRpdmVfRHVyYXRpb24gKyBEYXlzX1ByYWN0aWNlZCArIFN0YW5kYXJkLA0KICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhLA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnYm0iLA0KICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiLCAiQm94Q294IiksDQogICAgICAgICAgICAgICAgdHVuZUxlbmd0aCA9IDEwMCwNCiAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbi5jb250cm9sKQ0KIA0KI3NodXQgdGhlIGluc3RhbmNlcyBvZiBSIGRvd24NCnN0b3BDbHVzdGVyKGNsKQ0KDQojY29tcGFyZSBtb2RlbHMNCm1vZGVsX2xpc3QgPC0gbGlzdChvbmUgPSBtb2RlbCwgdHdvID0gbW9kZWwyLCB0aHJlZSA9IG1vZGVsMywgZm91ciA9IG1vZGVsNCwgZml2ZSA9IG1vZGVsNSwgc2l4ID0gbW9kZWw2LCBzZXZlbiA9IG1vZGVsNykNCg0KbW9kZWxfY29tcGFyaXNvbiA8LSByZXNhbXBsZXMobW9kZWxfbGlzdCkNCg0Kc3VtbWFyeShtb2RlbF9jb21wYXJpc29uKQ0KDQojIEVzdGltYXRlIGFjY3VyYWN5IGJhc2VkIG9uIGRpZmZlcmVudCBncm91cHM/IHdoeSBkb2VzIHRoZSBtb2RlbCBwZXJmb3JtIGJhZGx5IHRoZXJlDQojIGtlZXAgTE0gbW9kZWwgZm9yIGV4cGxhbmF0aW9uIG9yIGV2ZW4gUkYNCg0KIyBjb3JyZWxhdGlvbg0KIyBsZWFybmluZyBjdXJ2ZXMgdG8gaW5kaWNhdGUgb3ZlcmZpdHRpbmcgYW5kIHVuZGVyZml0dGluZw0KDQojIHRyYW5zZm9ybSBkYXlzX3ByYWN0aWNlZCBpbnRvIHNvbWV0aGluZyBtb3JlIGxpa2UgMS0yLTMgYmFzZWQgb24gMTIwIGRheXM/IHdoeSBpcyBjaG9waW4gc28gaGlnaCANCg0KIyBoeXBlciBwYXJhbWV0ZXJzIA0KIyBodHRwczovL3RvcGVwby5naXRodWIuaW8vY2FyZXQvbW9kZWwtdHJhaW5pbmctYW5kLXR1bmluZy5odG1sI21vZGVsLXRyYWluaW5nLWFuZC1wYXJhbWV0ZXItdHVuaW5nDQojIGh0dHBzOi8vdG9wZXBvLmdpdGh1Yi5pby9jYXJldC9yYW5kb20taHlwZXJwYXJhbWV0ZXItc2VhcmNoLmh0bWwNCmBgYA0KDQpXZSBjaG9zZSB0aGUgUmFuZG9tIEZvcmVzdCBtb2RlbCBhcyBpdCB3YXMgdGhlIGJlc3QgcGVyZm9ybWluZyBtb2RlbC4gSXQgaXMga25vd24gYXMgYSBtb2RlbCB3aGljaCBpczoNCg0KKiBub3QgdmVyeSBzZW5zaXRpdmUgdG8gb3V0bGllcnMNCiogZ29vZCBmb3Igbm9uLWxpbmVhcml0eQ0KKiB2YXJpYWJsZSBpbXBvcnRhbmNlIGNhbiBiZSBiaWFzZWQgaWYgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGhhdmUgZmV3IGxldmVscyAodG93YXJkIGhpZ2ggbGV2ZWxzKSBvciBhcmUgY29ycmVsYXRlZA0KDQojIyMgQWN0dWFscyB2cyBQcmVkaWN0aW9ucw0KDQpgYGB7cn0NCnNlbGVjdGVkX21vZGVsIDwtIG1vZGVsNQ0KDQojU2F2aW5nIHRoZSBtb2RlbA0Kc2F2ZVJEUyhzZWxlY3RlZF9tb2RlbCwgZmlsZSA9ICJtb2RlbC5yZGEiKQ0KDQojZ2V0IHByZWRpY3Rpb25zDQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHNlbGVjdGVkX21vZGVsLCBtb2RlbF9kYXRhKQ0KDQojY3JlYXRlIGRhdGFzZXQNCm1vZGVsX2RhdGEyIDwtIG1vZGVsX2RhdGENCm1vZGVsX2RhdGEyJFByZWRpY3RlZCA8LSBwcmVkaWN0aW9ucw0KbW9kZWxfZGF0YTIkQWN0dWFsIDwtIG1vZGVsX2RhdGEkRHVyYXRpb24NCm1vZGVsX2RhdGEyJFJlc2lkdWFscyA8LSBtb2RlbF9kYXRhMiRBY3R1YWwgLSBtb2RlbF9kYXRhMiRQcmVkaWN0ZWQNCg0KIyBtb2RlbF9kYXRhMiA8LSBtb2RlbF9kYXRhJT4lDQojICAgbXV0YXRlKEFjdHVhbCA9IGFzLm51bWVyaWMoRHVyYXRpb24pLA0KIyAgICAgICAgICBQcmVkaWN0ZWQgPSBhcy5udW1lcmljKHByZWRpY3Rpb25zKSwNCiMgICAgICAgICAgUmVzaWR1YWxzID0gQWN0dWFsIC0gUHJlZGljdGVkKSU+JQ0KIyAgIHNlbGVjdChQcmVkaWN0ZWQsIEFjdHVhbCwgUmVzaWR1YWxzLCBQcm9qZWN0LCBMZXZlbCwgR2VucmUpDQoNCiN2aXN1YWxpc2UgcHJlZGljdGVkIHZzIGFjdHVhbA0KZ2dwbG90bHkoDQpnZ3Bsb3QobW9kZWxfZGF0YTIsIGFlcyhQcmVkaWN0ZWQsIEFjdHVhbCwgbGFiZWwgPSBSZXNpZHVhbHMsIGNvbCA9IExldmVsKSkrDQogIGdlb21fcG9pbnQoYWVzKHRleHQgPSBQcm9qZWN0KSkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIGNvbCA9ICJyZWQiLCBsd2QgPSAxLCBzZSA9IEZBTFNFKSsNCiAgZ2VvbV9hYmxpbmUobHR5ID0gImRhc2hlZCIsIGx3ZCA9IDAuNSwgY29sID0gImdyYXkiKSsNCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsNTApLCB5bGltID0gYygwLDUwKSkrDQogIGxhYnMoY29sID0gTlVMTCkrDQogIHNjYWxlX2NvbG9yX3Ryb24oKSsNCiAgdGhlbWVfaXBzdW1fZXMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KKSAlPiUNCiAgbGF5b3V0KGxlZ2VuZCA9IGxpc3QoDQogICAgICBvcmllbnRhdGlvbiA9ICJoIiwNCiAgICAgIHggPSAwLjQsIHkgPSAxLjIpKQ0KYGBgDQoNCiMjIyBSZXNpZHVhbCBkaXN0cmlidXRpb24NCg0KV2UgY2FuIHNlZSB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIG1vc3RseSBzaXR1YXRlZCBhcm91bmQgMC4NCg0KYGBge3J9DQpnZ3Bsb3QobW9kZWxfZGF0YTIsIGFlcyhSZXNpZHVhbHMsIGZpbGwgPSAuLmNvdW50Li4pKSsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBjb2wgPSAiYmxhY2siKSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1tZWFuKFJlc2lkdWFscykpLCBsd2QgPSAxLCBsdHkgPSAyKSArDQogIGxhYnMoeD0iUmVzaWR1YWxzIiwNCiAgICAgICB5PSAiVG90YWwgb2NjdXJlbmNlcyIpKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ieWVsbG93IiwgaGlnaD0icmVkIikrDQogIHRoZW1lX2lwc3VtX2VzKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIyMgQWN0dWFsIHZlcnN1cyBSZXNpZHVhbHMNCg0KTGFzdGx5LCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgaXMgYSBjb25zdGFudCB2YXJpYWJpbGl0eSBvZiBlcnJvcnMuIEhvd2V2ZXIsIHRoZXJlIGlzIHN0aWxsIGEgdGVuZGVuY3kgdG8gdW5kZXJwcmVkaWN0IGZvciBwaWVjZXMgdGhhdCB0b29rIHZlcnkgbGl0dGxlIGFuZCBvdmVyIHByZWRpY3QgcmVxdWlyZWQgdGltZSBmb3IgcGllY2VzIHRoYXQgdG9vayBsb25nZXIgdGhhbiBuZWNlc3NhcnkuDQoNCmBgYHtyfQ0KZ2dwbG90bHkoDQpnZ3Bsb3QobW9kZWxfZGF0YTIsIGFlcyhBY3R1YWwsIFJlc2lkdWFscywgY29sID0gTGV2ZWwsIGxhYmVsID0gUHJlZGljdGVkKSkrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIHNpemUgPSAzLCBjb2xvciA9ICJncmV5NTIiKSsNCiAgZ2VvbV9wb2ludChhZXModGV4dCA9IFByb2plY3QpLCBhbHBoYSA9IDAuNSkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIGNvbCA9ICJyZWQiLCBzZSA9IEZBTFNFKSsNCiAgbGFicyhjb2wgPSBOVUxMKSsNCiAgc2NhbGVfY29sb3JfdHJvbigpKw0KICB0aGVtZV9pcHN1bV9lcygpDQopICU+JQ0KICBsYXlvdXQobGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICJoIix4ID0gMC40LCB5ID0gMS4yKSkNCmBgYA0KDQojIyBXaGljaCBtb2RlbCBwZXJmb3JtZWQgYmV0dGVyPw0KDQpgYGB7cn0NCnRpZHkoY29tcGFyZV9tb2RlbHMobW9kZWw0LCBtb2RlbDUpKSU+JQ0KICBrYmwoY2FwdGlvbiA9ICJNb2RlbCAxIHZzIG1vZGVsIDIiKSU+JSAjY2hhbmdlIHRoaXMNCiAga2FibGVfcGFwZXIoImhvdmVyIiwgZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KVGhlc2UgcmVzdWx0cyBhbHNvIGNvbmZpcm0gdGhhdCB0aGUgUmFuZG9tIEZvcnJlc3QgbW9kZWwgaXMgc2lnbmlmaWNhbnRseSBiZXR0ZXIgdGhhbiB0aGUgb3RoZXIgdHdvLg0KDQojIyBIb3cgbWFueSBwcmVkaWN0b3JzIGRpZCB0aGUgbW9zdCBvcHRpbWFsIG1vZGVsIGhhdmU/DQoNCmBgYHtyIHByZWRpY3RvcnN9DQpwbG90KG1vZGVsNSwgDQogICAgIG1haW4gPSAiVGhlIG1vc3Qgb3B0aW1hbCBtb2RlbCB3YXMgdGhhdCB3aXRoIDUgcHJlZGljdG9ycyIsIGNvbCA9ICJvcmFuZ2UiLCBsd2QgPSAzKQ0KYGBgDQoNCiMjIFdoYXQgd2VyZSB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzPw0KDQpgYGB7ciBmYWN0b3JzfQ0KaW1wIDwtIHZhckltcChtb2RlbDUpDQoNCmdncGxvdChpbXAsIHNpemUgPSA2KSsNCiAgZ2VvbV9jb2woY29sID0gIndoaXRlIiwgZmlsbCA9ICJ3aGl0ZSIpKw0KICBnZW9tX3NlZ21lbnQoYWVzKEZlYXR1cmUsIHkgPSAwLCB4ZW5kID0gRmVhdHVyZSwgeWVuZCA9IEltcG9ydGFuY2UpLCBjb2wgPSAiYmxhY2siLCBzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSA4LCBjb2wgPSAib3JhbmdlIikrDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZShyb3VuZChJbXBvcnRhbmNlKSwgIiUiLCBzZXAgPSAiIikpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzLCBjaGVja19vdmVybGFwID0gVFJVRSkrDQogIHNjYWxlX2NvbG9yX3Ryb24oKSsNCiAgc2NhbGVfZmlsbF90cm9uKCkrDQogIHRoZW1lX2lwc3VtX2VzKCkrDQogIGxhYnModGl0bGUgPSAiVmFyaWFibGUgaW1wb3J0YW5jZSByYW5raW5nIikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gIGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpDQoNCiNjb21wYXJlIHdpdGggbGluZWFyIHJlZ3Jlc3Npb24NCmBgYA0KDQojIExpbWl0YXRpb25zDQoNCiogdmVyeSAqKmxpbWl0ZWQgZGF0YSoqLCBob3dldmVyLCBhIGNyb3NzIGZvbGQgdmFsaWRhdGlvbiBpcyBrbm93biB0byBiZSBhIGdvb2Qgc3Vic3RpdHV0ZSB0byBhIHRyYWluL3Rlc3Qgc3BsaXQNCiogcGFydGljdWxhciB0byAqKm9uZSBwZXJzb24qKidzIHBpYW5vIGFiaWxpdHkgKG90aGVycyBtaWdodCBsZWFybiBmYXN0ZXIgb3Igc2xvd2VyKQ0KKiAqKnF1YWxpdHkgb2YgcHJhY3RpY2UqKiBpcyBhIHNpZ25pZmljYW50IGZhY3RvciB3aGljaCBpcyBub3QgY2FwdHVyZWQgaW4gdGhpcyBkYXRhc2V0DQoqIHZlcnkgKipkaWZmaWN1bHQgdG8gYXNzZXNzIHdoZW4gYSBwaWVjZSBpcyAiZmluaXNoZWQiKiogYXMgeW91IGNhbiBhbHdheXMgZnVydGhlciBpbXByb3ZlIG9uIHlvdXIgaW50ZXJwcmV0YXRpb24gb2YgYSBwaWVjZQ0KKiBub3QgYWxsIHBpZWNlcyBoYWQgb2ZmaWNpYWwgKipBQlJTTSByYXRpbmdzKiosIGFuZCBldmVuIGZvciB0aG9zZSB0aGF0IGRpZCwgaXQncyBzdGlsbCBoaWdobHkgc3ViamVjdGl2ZS4gRnVydGhlcm1vcmUsIGl0IGlzIGhhcmQgdG8gY2FwdHVyZSBhbGwgdGhlIGRpZmZpY3VsdHkgb2YgYSBwaWVjZSBpbiBvbmUgIm51bWJlciINCiogZXZlbiB3aXRoIHBlcmZlY3QgZ3JhZGluZy4uLg0KKiB0aGUgdGltZSBpdCB0b29rIG1lIHRvIGxlYXJuIGEgcGllY2UgaXMgYmlhc2VkIHRvd2FyZHMgdGhlIGN1cnJlbnQgc3RhZ2Ugb2YgcHJvZ3Jlc3MuIEFzIG15IHNraWxsIGltcHJvdmVkLCBpdCBpcyBsaWtlbHkgdGhhdCB0aGUgaG91cnMgcmVxdWlyZWQgdG8gbGVhcm4gYSBwaWVjZSByZW1haW5lZCBjb25zdGFudCBkdWUgdG8gdGhlIGluY3JlYXNlZCBkaWZmaWN1bHR5IG9mIHRoZSBwaWVjZS4gQXQgdGhlIHNhbWUgdGltZSwgYSBwaWVjZSB0aGF0IHRvb2sgMjAgaG91cnMgdG8gbGVhcm4gYSBmZXcgeWVhcnMgYWdvIGNvdWxkIG9ubHkgdGFrZSBtZSA1LTEwIGhvdXJzIG5vdw0KKiAqKm1lbW9yaXNhdGlvbioqIHNvbWV0aW1lcyB0aGVyZSdzIGFuIGVmZm9ydCB0byBwcmFjdGljZSBhIGJpdCBmb3IgbG9uZ2VyIGp1c3QgdG8gbWVtb3Jpc2Ugd2l0aG91dCBhbiBpbXByb3ZlbWVudCBpbiBwZXJmb3JtYW5jZSB3aGljaCBpcyBub3QgY2FwdHVyZWQgaW4gdGhpcyBhbmFseXNpcw0KDQojIEFwcGxpY2F0aW9uOg0KDQoqIHlvdSBjYW4gZmluZCBhbiBpbnRlcmFjdGl2ZSBkaXNwbGF5IG9mIHRoaXMgcHJlc2VudGF0aW9uLCBhcyB3ZWxsIGFzIHRoZSBtb2RlbCBpbiBwcm9kdWN0aW9uIGF0IHRoZSBiZWxvdyBsaW5rDQoqIGxpbms6IHd3dy5wZXRlcmhvbnRhcnUuY29tDQogDQojIEhhcmRlc3QgdGhpbmdzIGFib3V0IHRoaXMgYW5hbHlzaXM6DQoNCiogdGhlIEV4dHJhY3QtVHJhbnNmb3JtLUxvYWQgcHJvY2VzcyAtIGZpbmRpbmcgY3JlYXRpdmUgd2F5cyB0byBpbnB1dCB0aGUgZGF0YSBvbiB0aGUgZnJvbnQgZW5kIG9mIHRoZSBhcHAgdG8gbWFrZSBpdCByZXBvcnRpbmcgZnJpZW5kbHkgb24gdGhlIGJhY2tlbmQgKHdpdGggYWxsIHRoZSB2YXJpYWJsZXMgc3VjaCBhcyBHZW5yZSwgVHlwZSBvZiBwcmFjdGljZSwgQ29tcG9zZXIgYW5kIFBpZWNlIG5hbWUsIHRhZyBwaWVjZXMgYXMgIndvcmsgaW4gcHJvZ3Jlc3MiIGV0YykNCiogYXV0b21hdGUgd2F5cyB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gcGllY2VzIHRoYXQgSSBjYW1lIGJhY2sgdG8gKHJlcGVhdCB2YXJpYWJsZSkgdnMgcGllY2VzIEkgb25seSBzdHVkaWVkIG9uY2UNCiogY2xlYW4gYWxsIHRoZSAiZGlydHkiIGRhdGENCiogZGV2ZWxvcCBhIGZhaXJseSBhY2N1cmF0ZSBhbGdvcml0aG0gYmFzZWQgb24gdmVyeSBsaW1pdGVkIGRhdGENCg0KIyBTdW1tYXJ5IC8gd2hhdCBkaWQgSSBsZWFybj8NCg0KKiBwcmFjdGljZWQgbGVzcyBpbiBsb2NrZG93bg0KKiBpbXBhY3Qgb2Ygc29tZXRoaW5nIGxpa2Ugc2lnaHQgcmVhZGluZyBvbiBsZWFybmluZyBhIHBpZWNlIGZhc3RlciAvIHRlY2huaXF1ZSB3b3JrICh3aGljaCBJIG5vdyB0cmFjaykNCiogdGVzdGluZyBvbiBjdXJyZW50IHBpZWNlcw0KDQpXaGF0J3MgbmV4dD8NCg0KKiBjb21pbmcgYmFjayB0byBpdCBvbmNlIEkgZ2V0IG1vcmUgZGF0YSwgZXNwZWNpYWxseSBvZiBoYXJkZXIgcGllY2VzDQoNCiMgT3RoZXINCg0KKiBHYW50dCBjaGFydCBmb3IgZWFjaCBwaWVjZSB8fHx8fCB8fCB8fHx8fHx8IG1hcCBmb3IgcHJhY3RpY2Ugc2Vzc2lvbnMgYnkgdGltZSAobWF5YmUgbG93IGludGVuc2l0eSBiYWNrZ3JvdW5kKQ0KKiBtZXNzYWdlIENsYXVkaWEgQ2FydGF5YSAtIGJpa2Ugc2hhcmluZyBhcHANCiogZWFjaCBncmFwaCBzaG91bGQgaGF2ZSBhIHRpdGxlDQoqIEZpbmQgYSBwaWVjZSB0byBwbGF5IChyZWNvbW1lbmRlciBvcHRpb24pDQoqIFdoYXQgYW0gaSBjdXJyZW50bHkgcHJhY3RpY2luZyB0YWJsZSBuZXh0IHRvIGRhc2hib2FhcmQ/ICsgCWdyYXBoIExhc3QgZmV3IHNlc3Npb21zIHRhYmxlDQoqIERvbuKAmXQgdXNlIGNvbW1lbnRzIHRvIHNheSB3aGF0L2hvdyB5b3VyIGNvZGUgaXMgZG9pbmcsIHVzZSBpdCB0byBkZXNjcmliZSB3aHkuIE90aGVyd2lzZSwgeW91IGhhdmUgdG8gcmVtZW1iZXIgdG8gY2hhbmdlIGNvbW1lbnRzIHdoZW4geW91IGNoYW5nZSB5b3VyIGNvZGUu